理解React的生命周期

本文主要探讨React组件在状态更新时的生命周期方法执行顺序。通过一个demo演示了挂载、更新和卸载阶段的方法调用,揭示了在props或state改变时,组件的生命周期行为。在更新阶段,通过shouldComponentUpdate的条件判断避免不必要的渲染。
摘要由CSDN通过智能技术生成

本系列文章是本人学习相关知识时所积累的笔记,以记录自己的学习历程,也为了方便回顾知识;故文章内容较为随意简练,抱着学习目的来的同学务必转移他处,以免我误人子弟~

理解React的生命周期

参考资料
React官网:State and Lifecycle
React官网:React.Component
微信公众号(译文):如何通俗易懂地解释React生命周期方法?
公众号原文:The (new) React lifecycle methods in plain, approachable language

  1. 挂载(mount)
  2. 更新(update)
  3. 卸载(unmount)
  4. 错误处理(error handle)
  • 与mounting相关的生命周期方法:
    render()
    constructor(props)
    componentDidMount()
    static getDerivedStateFromProps(props, state)
  • 与updating相关的:
    static getDerivedStateFromProps(props, state)
    shouldComponentUpdate(nextProps, nextState)
    render()
    getSnapshotBeforeUpdate(prevProps, prevState)
    componentDidUpdate(prevProps, prevState, snapshot)
  • 与unmounting相关的:
    componentWillUnmount
  • 与error handling相关的:
    static getDerivedStateFromError(error)
    componentDidCatch(error, info)

各个方法的作用在以上文档资料中已经讲解地很详细,这里不再赘述,这篇文章主要讨论组件在状态更新(props或state改变)时,生命周期方法的执行顺序

为此我创建一个demo:react-lifecycle-demo

  1. 下载到本地,首先在项目目录下运行命令yarn安装所需要的依赖包
  2. 运行yarn start命令,启动项目
  3. F12打开控制台查看打印结果

实验结果

页面代码:

// main.js 入口文件
import * as React from "react";
import { render } from "react-dom";
import HelloWorld, { ErrorBoundary } from "./component";

class App extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            num: 1,
        }
        this.add = this.add.bind(this);
    }

    add() {
        this.setState((state) => {
            return { num: state.num + 1 };
        })
    }
    
    render() {
        return (
            <div>
                <ErrorBoundary>
                    {
                        this.state.num < 4
                            ? <HelloWorld num={this.state.num} />
                            : <div>HelloWorld Component Unmounted</div>
                    }
                    <span style={{ color: "white", backgroundColor: "skyblue", cursor: "pointer" }} onClick={this.add}>+</span>
                </ErrorBoundary>
            </div>
        )
    }
}

render(<App />, document.querySelector("#root"));
// component.js
import * as React from "react";

export default class HelloWorld extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            text: "HelloWorld!",
        }
        console.log("constructor");
    }
    componentDidMount() {
        console.log("DidMount");
    }
    static getDerivedStateFromProps(state, props) {
        console.log("getDerivedStateFromProps");
        return { text: "from getDerivedStateFromProps" }
    }
    shouldComponentUpdate(nextProps, nextState) {
        console.log("shouldComponentUpdate");
        if (nextProps.num < 3) {
            return true;
        } else {
            return false;
        }
    }
    getSnapshotBeforeUpdate(prevProps, prevState) {
        console.log("getSnapshotBeforeUpdate");
        return "snapshot from getSnapshotBeforeUpdate";
    }
    componentDidUpdate(prevProps, prevState, snapshot) {
        console.log("DidUpdate");
        console.log(snapshot);
    }
    componentWillUnmount() {
        console.log("WillUnmount")
    }

    render() {
        console.log("render");
        return (
            <div>{`${this.props.num ? this.props.num : "none"} | ${this.state.text}`}</div>
        )
    }
}

export class ErrorBoundary extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            hasError: false,
        };
    }
    static getDerivedStateFromError(error) {
        console.log("getDerivedStateFromError");
        return { hasError: true };
    }
    componentDidCatch(error, info) {
        console.log("componentDidCatch");
    }
    render() {
        return (
            this.state.hasError
                ? <div>Something has went wrong</div>
                : this.props.children
        )
    }
}
  1. 启动项目后,页面显示:
    启动项目后
    控制台打印:
> constructor
> getDerivedStateFromProps
> render
> DidMount
  • 由此可知组件构建的经过的生命周期顺序
  • state.text 属性从“HelloWorld!”变为“from getDerivedStateFromProps
  • getDerivedStateFromProps在组件首次构建时先于render,返回的对象用于更新组件的state
  1. 第一次点击“+”

页面显示:
第一次点击
控制台打印:

> getDerivedStateFromProps
> shouldComponentUpdate
> render
> getSnapshotBeforeUpdate
> DidUpdate
> snapshot from getSnapshotBeforeUpdate

第一次点击“+”,更新了父组件Appstatenum属性,进而更新了子组件HelloWorldprops,所以第一次点击属于HelloWorld组件的更新阶段

  • 从控制台的打印结果可以知道组件updating时生命周期方法的调用顺序
  • 更新阶段constructorcomponentDidMount不调用
  • getSnapshotBeforeUpdaterender之后调用,即页面组件渲染完成后
  1. 第二次点击
    页面:
    img
    控制台:
> getDerivedStateFromProps
> shouldComponentUpdate
  • 第二次仍然是更新阶段,此时父组件app的state的num是3,子组件的props的num也为3,但是因为在shouldComponentUpdate方法中进行了条件判断,当this.props.num不小于3时即返回false,所以组件不再更新,则render,getSnapshotBeforeUpdate,componentDidUpdate等方法不再被调用
  1. 第三次点击
    页面:
    img
    控制台:
> WillUnmount
  • 第三次点击时组件的卸载阶段,因为父组件的state的num不小于4时,即卸载HelloWorld组件,展示<div>HelloWorld Component Unmounted</div>元素
  • 卸载阶段仅调用componentWillUnmount
setState()

setState能触发组件更新

假设我们在子组件的componentDidMount中使用setState来更新其state的text值为“setState from componentDidMount”,组件的构建过程是什么样的呢?

修改的代码如下:

    componentDidMount() {
        console.log("DidMount");
+       this.setState({
+           text: "setState from componentDidMount",
+       })
    }
    static getDerivedStateFromProps(state, props) {
        console.log("getDerivedStateFromProps");
-       return { text: "from getDerivedStateFromProps" }
+       return null;
    }

重新启动项目,得到页面:
img
控制台:

> constructor
> getDerivedStateFromProps
> render
> DidMount
> getDerivedStateFromProps
> shouldComponentUpdate
> render
> getSnapshotBeforeUpdate
> DidUpdate
> snapshot from getSnapshotBeforeUpdate

"DidMount"被打印出来后,紧接着进行setState方法,此时组件进入updating阶段,组件被重新渲染(已挂载),所以经历的周期函数与this.props更新是一样的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值