关于react生命周期的总结

3次阅读

共计 5193 个字符,预计需要花费 13 分钟才能阅读完成。

写在前边

这是一片记录 react 生命周期的文章。博主最近在重读 react 官网,想对 react 有更深度的理解。如果你同样对 react 这个优秀的框架抱有兴趣,欢迎联系我一同探讨!!文中有描述含混不清和错误的地方,望不吝赐教,直接指出

文末附有验证生命周期 API 先后顺序的 demo

QQ: 272473132
github: https://github.com/CregskiN

1. 基础概念

Mount、Update、Unmount

  • Mounting:组件初次渲染
  • Updating:因某些原因,组件需要更新
  • Unmounting:组件卸载。即他的父组件的 render() 函数中不再 return 他

渲染阶段、预提交阶段、提交阶段

  • Render phase:react 负责将 JSX 渲染为 DOM 对象(仅渲染,不更新页面)

一旦 state 或 props 改变,Render phase 阶段将被强制重新执行(具体次数无法预计)。所以尽量不要 Render phase 更新 state 或 props。

  • Pre-commit phase:react 渲染完 DOM,预提交阶段,在这里,可以读取 DOM 信息
  • Commit phase:react 渲染并完成 DOM 更新

官方建议:仅在这个阶段执行副作用、state 更新

2. Mounting 阶段

Render phase

  • constructor()

    • 初始化 state、props

      • 初始化 state 不要用 props.color,可以直接用 this.props.color
    • 为事件处理 绑定 this
  • static getDerivedStateFromProps(props, state)

    • 用处罕见:派生状态组件,即 state 在任何时候都取决于 prop

      不建议使用派生组件,会导致受控于非受控含混不清,state 混乱

    • return newState 返回对象以更新 state,若返回 null,不更新任何内容(无法使用 this.state 修改 state)
  • render()

    render 被调用时,它会检查 this.propsthis.state 的变化并返回以下类型之一:

    • React 元素 。通常通过 JSX 创建。例如, 会被 React 渲染为 DOM 节点, 会被 React 渲染为自定义组件,无论是 还是 均为 React 元素。
    • 数组或 fragments。使得 render 方法可以返回多个元素。欲了解更多详细信息,请参阅 fragments 文档。
    • Portals。可以渲染子节点到不同的 DOM 子树中。欲了解更多详细信息,请参阅有关 portals 的文档
    • 字符串或数值类型 。它们在 DOM 中会被渲染为文本节点
    • 布尔类型或 null。什么都不渲染。(主要用于支持返回 test && 的模式,其中 test 为布尔类型。)

Pre-commit phase

Commit phase

  • componentDidMount()

    • this.setState() 触发额外渲染,但会在浏览器更新屏幕之前
    • 副作用 (side effect)

3. Updating 阶段

当组件的 props 或 state 发生变化时会触发更新。组件更新的生命周期调用顺序如下:

Render phase

  • static getDerivedStateFromProps()
  • shouldComponentUpdate(nextProps, nextState)

    • return false ? 不调用 Updating 阶段的 render() : 调用 Updating 阶段的 render();
  • render()

Pre-commit phase

  • getSnapshotBeforeUpdate(prevProps, prevState)

    • 在以下场景调用:

      • 想要在组件改变之前获取 DOM 信息,如滚动位置

博主疑问:

为什么只有 Updating 阶段有 getSnapshotBeforeUpdate 而 Mounting 阶段没有?

是因为在 Mounting 阶段读取 DOM 的需求吗?

还是说官方建议 Mounting 阶段读取 DOM 的逻辑放到 componentDidMount 执行?

Commit phase

  • componentDidUpdate(prevProps, prevState, snapshot)

    • this.setState() 额外渲染,影响性能!!而且一定用 if 包裹,否则会死循环

      componentDidUpdate(prevProps) {
        // 典型用法(不要忘记比较 props):if (this.props.userID !== prevProps.userID) {this.fetchData(this.props.userID);
        }
      }

4. Unmounting 阶段

当组件从 DOM 中移除时会调用如下方法:

Render phase

Pre-commit phase

Commit phase

  • componentWillUnmount()

5. other lifecycle

错误处理

当渲染过程,生命周期,或子组件的构造函数中抛出错误时,会调用如下方法:

Render phase

  • static getDerivedStateFromError(error)

    • return newState;

Commit phase

  • componentDidCatch(error, info)

    • error:Error 实例
    • info:React.ErrorInfo 包含错误栈信息

父组件 Lifecycle.tsx

import React, {Component} from 'react';

import CanUnmount from './CanUnmount';

interface LifecycleProps { };
interface LifecycleState {counter: number;};

function tableLog(logName: string, obj: any) {if (obj !== null && obj !== undefined) {if (typeof obj === 'object' && obj.length) {console.log(logName);
            console.table(obj);
        }
    }
}


class lifecycle extends Component<LifecycleProps, LifecycleState> {handleAdd() {this.setState((state, props) => ({counter: state.counter + 1}))
    }

    handleLess() {this.setState((state, props) => ({counter: state.counter - 1}))
    }

    handleReset() {this.setState((state, props) => ({counter: 0}))
    }

    // Mounting
    constructor(props: LifecycleState) {super(props);
        this.state = {counter: 0,};

        console.group({lifecycle: 'constructor'});
        console.table(this.state)
        console.groupEnd();

        this.handleAdd = this.handleAdd.bind(this);
        this.handleLess = this.handleLess.bind(this);
        this.handleReset = this.handleReset.bind(this);
    }

    static getDerivedStateFromProps(props: LifecycleProps, state: LifecycleState) {
        // render-phase 获得来自副组件的 props 派生
        console.group({lifecycle: 'getDerivedStateFromProps'});
        tableLog('props', props);
        tableLog('state', state);
        console.groupEnd();
        return null;
    }

    render() {console.group({ lifecycle: 'render'});

        console.groupEnd();
        const {counter} = this.state;
        const {handleAdd, handleLess, handleReset} = this;

        if (counter < 3) {
            return (
                <div>
                    {counter}
                    <button onClick={handleAdd}>+1</button>
                    <button onClick={handleLess}>-1</button>
                </div>
            )
        } else {
            return (
                <div>
                    {counter}
                    <button onClick={handleReset}>reset</button>
                    <CanUnmount />
                </div>
            )
        }


    }

    componentDidMount() {console.group({ lifecycle: 'componentDidMount'});
        console.log('Mounting finished')
        console.groupEnd();
        console.log('\n');
    }

    // Updating
    // static getDerivedStateFromProps

    shouldComponentUpdate(nextProps: LifecycleProps, nextState: LifecycleState) {console.group({ lifecycle: 'shouldComponentUpdate'});
        tableLog('nextProps', nextProps);
        tableLog('nextState', nextState);
        console.groupEnd();
        if (this.state.counter !== nextState.counter) {return true;}
        return false;
    }

    // render

    getSnapshotBeforeUpdate(prevProps: LifecycleProps, prevState: LifecycleState) {console.group({ lifecycle: 'getSnapshotBeforeUpdate'});
        tableLog('prevProps', prevProps);
        tableLog('prevState', prevState);
        console.log('getSnapshotBeforeUpdate finished');
        console.groupEnd();
        return {snapshot: 'ss'};
    }

    componentDidUpdate(prevProps: LifecycleProps, prevState: LifecycleState, snapshot: any) {console.group({ lifecycle: 'componentDidUpdate'});
        tableLog('prevProps', prevProps);
        tableLog('prevState', prevState);
        tableLog('snapshot', snapshot);
        console.log('Update finished');
        console.groupEnd();
        console.log('\n');
    }

    // Upmounting
    componentWillUnmount() {console.group({ lifecycle: 'componentWillUnmount'});
        console.log('Unmounting finished');
        console.groupEnd();
        console.log('\n');
    }


}

export default lifecycle;

子组件 CanUnmoun.tsx

import React, {Component} from 'react';

interface CanUnmounProps { };

class CanUnmoun extends Component {componentWillUnmount() {console.group({ lifecycle: 'CanUnmount componentWillUnmount'});
        console.log('CanUnmount Unmounting finished');
        console.groupEnd();
        console.log('\n');
    }

    render() {

        return (
            <div>
                Component CanUnmoun
            </div>
        )
    }
}

export default CanUnmoun;

Reference

React.Component

快速了解 React 的新功能 Suspense 和 Hooks

正文完
 0