react之生命周期API的总结

1,297 阅读4分钟

写在前边

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

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

QQ: 272473132 github: 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

Pre-commit phase

博主疑问:

为什么只有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

5. other lifecycle

错误处理

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

Render phase

Commit phase


父组件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