React新的生命周期

761 阅读4分钟

这是一个不错的文章 (The (new) React lifecycle methods in plain, approachable language)[blog.logrocket.com/the-new-rea…] 作者: Ohans Emmanuel

下面一个生命周期说明的组件

import * as React from 'react';

interface lifeCycleState {
    update: boolean,
    pointer: number,
    list: number[]
}

class LifeCycle extends React.Component<any, lifeCycleState> {

    /**
     * 从props中获取派生state
     * 在初始挂载时将组件呈现给DOM之前,会调用此方法。
     * 本质上,这个方法允许组件根据props的变化更新其内部状态
     * 
     * @param nextProps 
     * @param prevState 
     * 
     * @return {Partial<S> | null}
     */
    static getDerivedStateFromProps(nextProps: Readonly<any>, prevState: any) {

        console.log('getDerivedStateFromProps', 'props= ', nextProps, ' state= ', prevState)
        return {
            // 不推荐使用这种方法。只是一个例子。无条件覆盖状态通常被认为是一个坏主意。
            pointer: 1000
        }
    }

    /**
     * 从Error中获取派生state
     * 
     * @param error 
     * 
     * @return {Partial<S> | null}
     */
    static getDerivedStateFromError (error: any) {
        console.error('getDerivedStateFromError', error)
        return null
    }

    constructor (props: any) { // 构造函数 
        super(props);
        console.log('constructor')
        this.state = {
            update: false,
            pointer: 10,
            list: []
        }
    }

    /**
     * 在挂载之前立即调用,并在"Component#render"之前调用。
     * 避免在此方法中引入任何副作用或订阅。
     * 注意:存在getSnapshotBeforeUpdate或getDerivedStateFromProps防止这被调用。
     * 
     * @deprecated 16.3, 改用componentDidMount或constructor;在react 17中将停止工作
     * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#initializing-state
     * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path
     */
    // componentWillMount () {
    //     console.log('componentWillMount')
    // }

    /**
     * 
     * 在挂载之前立即调用,并在"Component#render"之前调用。
     * 避免在此方法中引入任何副作用或订阅。
     * 
     * 此方法不会在React 17中停止工作。
     * 
     * 注意:存在getSnapshotBeforeUpdate或getDerivedStateFromProps防止这被调用。
     * 
     * @deprecated 16.3, 改用componentDidMount或constructor
     * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#initializing-state
     * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path
     */
    // UNSAFE_componentWillMount () {
    //     console.log("UNSAFE_componentWillMount");
    // }

    /**
     * 挂载组件后立即调用。在此处设置state将触发重新渲染。
     */
    componentDidMount () {
        console.log('componentDidMount')
    }

    /**
     * 当组件可能正在接收新的props时调用。
     * 即使props没有改变,React也可以调用它,所以一定要比较新的和现有的props,如果你只想处理变化。
     * 调用`Component#setState`通常不会触发此方法。
     * 
     * 注意:存在getSnapshotBeforeUpdate或getDerivedStateFromProps防止这被调用。
     * 
     * @param nextProps 
     * @param nextContext 
     * 
     * @deprecated 16.3, 使用 static getDerivedStateFromProps 代替; 在react 17中将停止工作
     * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#updating-state-based-on-props
     * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path
     */
    // componentWillReceiveProps (nextProps: Readonly<any>, nextContext: any) {
    //     console.log('componentWillReceiveProps ', nextProps);
    // }

    /**
     * 当组件可能正在接收新的props时调用。
     * 即使props没有改变,React也可以调用它,所以一定要比较新的和现有的props,如果你只想处理变化。
     * 调用`Component#setState`通常不会触发此方法。
     * 
     * 此方法不会在React 17中停止工作。
     * 
     * 注意:存在getSnapshotBeforeUpdate或getDerivedStateFromProps防止这被调用。
     * 
     * @param nextProps 
     * @param nextContext 
     * 
     * @deprecated 16.3, 使用 static getDerivedStateFromProps 代替
     * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#updating-state-based-on-props
     * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path
     */
    // UNSAFE_componentWillReceiveProps (nextProps: Readonly<any>, nextContext: any) {
    //     console.log('UNSAFE_componentWillReceiveProps ', nextProps);
    // }

    /**
     * 在收到新的props或state时,在渲染之前立即调用。 
     * 初始渲染时不被调用。
     * 
     * 注意:你不能在这里调用`Component#setState`。
     * 注意:存在getSnapshotBeforeUpdate或getDerivedStateFromProps防止这被调用。
     * 
     * @param nextProps 
     * @param nextState 
     * @param nextContext 
     * 
     * @deprecated 16.3, 使用 getSnapshotBeforeUpdate 代替; 在react 17中将停止工作
     * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#reading-dom-properties-before-an-update
     * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path
     */
    // componentWillUpdate(nextProps: Readonly<any>, nextState: Readonly<any>, nextContext: any) {
    //     console.log('componentWillUpdate', nextProps, nextState, nextContext);
    // }

    /**
     * 在收到新的props或state时,在渲染之前立即调用。
     * 初始渲染时不被调用。
     * 
     * 注意:你不能在这里调用`Component#setState`。
     * 
     * 此方法不会在React 17中停止工作。
     * 
     * 注意:存在getSnapshotBeforeUpdate或getDerivedStateFromProps防止这被调用。
     * 
     * @param nextProps 
     * @param nextState 
     * @param nextContext 
     * 
     * @deprecated 16.3, 使用 getSnapshotBeforeUpdate 代替
     * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#reading-dom-properties-before-an-update
     * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path
     */
    // UNSAFE_componentWillUpdate(nextProps: Readonly<any>, nextState: Readonly<any>, nextContext: any) {
    //     console.log('UNSAFE_componentWillUpdate');
    // }

    /**
     * 调用以确定props和state的更改是否应触发重新渲染。
     * `Component`总是返回true。
     * `PureComponent`在props和state上实现浅层比较,如果props或state已经改变则返回true
     * 如果返回false,则为"Component#render","componentWillUpdate"并且不会调用`componentDidUpdate`。
     * 
     * @param nextProps 
     * @param nextState 
     * @param nextContext 
     * 
     * @return {boolean} 控制组件是否更新 true更新 false不更新
     */
    shouldComponentUpdate (nextProps: Readonly<any>, nextState: Readonly<any>, nextContext: any) {
        console.log('shouldComponentUpdate', nextProps, nextState, nextContext);
        return true
    }

    /**
     * 在react将"render"的结果应用于document之前运行,以及
     * 返回要提供给ComponentDidUpdate的对象。对保存有用
     * "render"之前的滚动位置等操作会导致对其进行更改。
     * 
     * 注意:getSnapshotBeforeUpdate的存在可防止生命周期事件。
     * 
     * 这个生命周期和 `componentWillMount`、`UNSAFE_componentWillMount`、
     * `componentWillReceiveProps`、`UNSAFE_componentWillReceiveProps`、
     * `componentWillUpdate`和`UNSAFE_componentWillUpdate` 不能共存 否则报警告
     * 
     * @param prevProps 前属性
     * @param prevState 前状态
     * 
     * @return {SS | null}
     */
    getSnapshotBeforeUpdate(prevProps: Readonly<any>, prevState: Readonly<any>) {
        console.log('getSnapshotBeforeUpdate ', 'prevProps= ', prevProps, ' prevState= ', prevState);
        return null
    }

    /**
     * 更新发生后立即调用。 
     * 初始渲染时不被调用。
     * 仅当存在getSnapshotBeforeUpdate且返回非null时,才会显示snapshot(getSnapshotBeforeUpdate的返回值)。
     * 
     * @param prevProps 前属性
     * @param prevState 前状态
     * @param snapshot getSnapshotBeforeUpdate的返回值
     */
    componentDidUpdate (prevProps: Readonly<any>, prevState: Readonly<any>, snapshot?: any) { // 组件更新完成
        console.log('componentDidUpdate', snapshot);
    }

    /**
     * 捕获后代组件中生成的异常。
     * 未处理的异常将导致要卸载的整个组件树。
     * 
     * @param error
     * @param errorInfo
     */
    componentDidCatch (error: Error, errorInfo: React.ErrorInfo) {
        console.error('componentDidCatch error', error)
        console.error('componentDidCatch errorInfo', errorInfo)
    }

    /**
     * 在组件被销毁之前立即调用。
     * 在此方法中执行任何必要的清理,例如取消了网络请求,或清理了`componentDidMount`中创建的任何DOM元素。
     */
    componentWillUnmount () {
        console.log('componentWillUnmount')
    }


    handleClick = () => {
        this.setState({
            update: !this.state.update
        })
    }

    handleThrowError = () => {
        // throw new Error('手动抛出错误!')
        const {list} = this.state;
        list.push(12)

        this.setState({
            list
        })
    }

    render () { // 渲染组件
        console.log('component render')

        return (
            <div className='lifecycle'>
                lifecycle render {this.state.update? 'true' : 'false'}
                <button onClick={this.handleClick}>
                    click me
                </button>
                <img src="https://pic2.zhimg.com/v2-610ad32e1ed334b3b12026a845e83399_r.jpg" />
                <div>pointer: {this.state.pointer}</div>

                <button onClick={this.handleThrowError}>点击5次</button>
                <div>
                    <ul>
                        {
                            this.state.list.map((l: number, i: number) => {
                                if (this.state.list.length === 5) {
                                    throw new Error('出错了!')
                                }
                                return <li key={i}>{l}</li>
                            })
                        }
                    </ul>
                </div>
            </div>
        )
    }
}

export default LifeCycle