React新的生命週期

freeshine發表於2019-02-28

這是一個不錯的文章 (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

複製程式碼

相關文章