写在前边

这是一片记录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