React的生命周期详细讲解

news2025/1/18 11:59:46

什么是生命周期?

所谓的React生命周期,就是指组件从被创建出来,到被使用,最后被销毁的这么一个过程。而在这个过程中,React提供了我们会自动执行的不同的钩子函数,我们称之为生命周期函数。**组件的生命周期大致分为三个阶段:组件挂载阶段,组件更新阶段,组件销毁卸载阶段 **

生命周期执行顺序

挂载

  • constructor(构造函数在类组件中比较常见)
  • getDerivedStateFromProps
  • render(render函数)-----只能访问this.props和this.state,不允许修改状态和DOM输出
  • componentDidMount(组件挂载)-------成功render并渲染完成真实DOM之后触发,可以修改DOM

更新

  • getDerivedStateFromProps
  • shouldComponentUpdate
  • render(render函数)
  • getSnapshotBeforeUpdate
  • componentDidUpdate(组件更新)--------可以修改DOM

卸载

  • componentWillUnmount(组件销毁)

图解

image.png

生命周期详解

constructor()

constructor() 在React组件挂载之前被调用一次,在为React.Component子类实现构造函数时,应在其他语句之前调用 super()

super的作用:将父类的this对象继承给子类。

通常,React构造函数仅用于以下两种情况:

  • 来初始化函数内部 state
  • 为 事件处理函数 绑定实例

如果不初始化 state 或不进行方法绑定,则不需要写 constructor() , 只需要设置 this.state 即可

不能在 constructor()构造函数内部调用 this.setState(), 因为此时第一次 render()还未执行,也就意味DOM节点还未挂载

static getDerivedStateFromProps(nextProps, state)

getDerivedStateFromProps() 在调用 render方法之前调用,在初始化和后续更新都会被调用

返回值:需要返回一个对象来更新 state, 如果没有指定返回值,React 会发出警告;React 需要用这个返回值来更新/派生组件的 state;如果不需要,最好直接省略这个方法,否则需要返回 null不会进行更新

参数: 第一个参数为即将更新的 props值, 第二个参数为上一个状态的 state , 可以比较propsstate来加一些限制条件,防止无用的state更新,即 state 的值在任何时候都取决于 props。

注意:getDerivedStateFromProps 是一个静态函数,静态方法不依赖组件实例而存在。所以不能使用this, 也就是只能作一些无副作用的操作

至于为什么要这样做?请移步 Morgan大佬 - 知乎

值得一提的是,getDerivedStateFromProps 在更新和挂载两个阶段都会“出镜”。这是因为“派生 state”这种诉求不仅在 props 更新时存在,在 props 初始化的时候也是存在的。React 16 以提供特定生命周期的形式,对这类诉求提供了更直接的支持。

render()

render() 方法是class组件中唯一必须实现的方法,用于渲染dom, render()方法必须返回reactDOM

注意: 不要在 render 里面 setState, 否则会触发死循环导致内存崩溃

componentDidMount()

componentDidMount() 在组件挂载后 (插入DOM树后) 立即调用,componentDidMount() 是发送网络请求、启用事件监听方法、数据初始化的好时机,并且可以在 此钩子函数里直接调用 setState()

shouldComponentUpdate(nextProps, nextState)

shouldComponentUpdate() 在组件更新之前调用,可以控制组件是否进行更新, 返回true时组件更新, 返回false则不更新

包含两个参数,第一个是即将更新的 props 值,第二个是即将更新后的 state 值,可以根据更新前后的 props 或 state 来比较加一些限制条件,决定是否更新,进行性能优化

不建议在 shouldComponentUpdate() 中进行深层比较或使用 JSON.stringify()。这样非常影响效率,且会损害性能

不要 shouldComponentUpdate 中调用 setState(),否则会导致无限循环调用更新、渲染,直至浏览器内存崩溃

可以使用内置 PureComponent 组件替代

getSnapshotBeforeUpdate(prevProps, prevState)

getSnapshotBeforeUpdate() 在最近一次的渲染输出被提交之前调用。也就是说,在 render 之后,即将对组件进行挂载时调用。

它可以使组件在 DOM 真正更新之前捕获一些信息(例如滚动位置),此生命周期返回的任何值都会作为参数传递给 componentDidUpdate()。如不需要传递任何值,那么请返回 null

componentDidUpdate(prevProps, prevState, snapshot)

componentDidUpdate() 会在更新后会被立即调用。首次渲染不会执行

包含三个参数,第一个是上一次props值。 第二个是上一次state值。如果组件实现了 getSnapshotBeforeUpdate() 生命周期(不常用),第三个是“snapshot” 参数传递

可以进行前后props的比较进行条件语句的限制,来进行 setState() , 否则会导致死循环

componentWillUnmount()

componentWillUnmount() 在组件即将被卸载或销毁时进行调用。

此生命周期是取消网络请求、移除监听事件清理 DOM 元素清理定时器等操作的好时机

实例展示

下面代码的react版本是16.4.0, 会根据父子组件props改变,父组件卸载、重新挂载子组件,子组件改变自身状态state这几个操作步骤,对其生命周期的执行顺序进行讲解

组件代码展示

父组件:Parent.js
import React, { Component } from 'react';
import { Button } from 'antd';
import Child from './child';

const parentStyle = {
  padding: 40,
  margin: 20,
  backgroundColor: 'LightCyan',
};

const NAME = 'Parent 组件:';

export default class Parent extends Component {
  constructor() {
    super();
    console.log(NAME, 'constructor');
    this.state = {
      count: 0,
      mountChild: true,
    };
  }

  static getDerivedStateFromProps(nextProps, prevState) {
    console.log(NAME, 'getDerivedStateFromProps');
    return null;
  }

  componentDidMount() {
    console.log(NAME, 'componentDidMount');
  }

  shouldComponentUpdate(nextProps, nextState) {
    console.log(NAME, 'shouldComponentUpdate');
    return true;
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    console.log(NAME, 'getSnapshotBeforeUpdate');
    return null;
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    console.log(NAME, 'componentDidUpdate');
  }

  componentWillUnmount() {
    console.log(NAME, 'componentWillUnmount');
  }

  /**
   * 修改传给子组件属性 count 的方法
   */
  changeNum = () => {
    let { count } = this.state;
    this.setState({
      count: ++count,
    });
  };

  /**
   * 切换子组件挂载和卸载的方法
   */
  toggleMountChild = () => {
    const { mountChild } = this.state;
    this.setState({
      mountChild: !mountChild,
    });
  };

  render() {
    console.log(NAME, 'render');
    const { count, mountChild } = this.state;
    return (
      <div style={parentStyle}>
        <div>
          <h3>父组件</h3>
          <Button onClick={this.changeNum}>改变传给子组件的属性 count</Button>
          <br />
          <br />
          <Button onClick={this.toggleMountChild}>卸载 / 挂载子组件</Button>
        </div>
        {mountChild ? <Child count={count} /> : null}
      </div>
    );
  }
}

子组件: Child.js
import React, { Component } from 'react';
import { Button } from 'antd';

const childStyle = {
  padding: 20,
  margin: 20,
  backgroundColor: 'LightSkyBlue',
};

const NAME = 'Child 组件:';

export default class Child extends Component {
  constructor() {
    super();
    console.log(NAME, 'constructor');
    this.state = {
      counter: 0,
    };
  }

  static getDerivedStateFromProps(nextProps, prevState) {
    console.log(NAME, 'getDerivedStateFromProps');
    return null;
  }

  componentDidMount() {
    console.log(NAME, 'componentDidMount');
  }

  shouldComponentUpdate(nextProps, nextState) {
    console.log(NAME, 'shouldComponentUpdate');
    return true;
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    console.log(NAME, 'getSnapshotBeforeUpdate');
    return null;
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    console.log(NAME, 'componentDidUpdate');
  }

  componentWillUnmount() {
    console.log(NAME, 'componentWillUnmount');
  }

  changeCounter = () => {
    let { counter } = this.state;
    this.setState({
      counter: ++counter,
    });
  };

  render() {
    console.log(NAME, 'render');
    const { count } = this.props;
    const { counter } = this.state;
    return (
      <div style={childStyle}>
        <h3>子组件</h3>
        <p>父组件传过来的属性 count : {count}</p>
        <p>子组件自身状态 counter : {counter}</p>
        <Button onClick={this.changeCounter}>改变自身状态 counter</Button>
      </div>
    );
  }
}

界面展示

从五种组件状态改变的时机来探究生命周期的执行顺序

一、父子组件初始化

父子组件第一次进行渲染加载时:

控制台的打印顺序为:

  • Parent 组件: constructor()
  • Parent 组件: getDerivedStateFromProps()
  • Parent 组件: render()
  • Child 组件: constructor()
  • Child 组件: getDerivedStateFromProps()
  • Child 组件: render()
  • Child 组件: componentDidMount()
  • Parent 组件: componentDidMount()
二、子组件修改自身状态 state

点击子组件 [改变自身状态counter] 按钮,其 [自身状态counter] 值会 +1, 此时控制台的打印顺序为:

  • Child 组件: getDerivedStateFromProps()
  • Child 组件: shouldComponentUpdate()
  • Child 组件: render()
  • Child 组件: getSnapshotBeforeUpdate()
  • Child 组件: componentDidUpdate()
三、修改父组件中传入子组件的 props

点击父组件中的 [改变传给子组件的属性 count] 按钮,则界面上 [父组件传过来的属性 count] 的值会 + 1,控制台的打印顺序为:

  • Parent 组件: getDerivedStateFromProps()
  • Parent 组件: shouldComponentUpdate()
  • Parent 组件: render()
  • Child 组件: getDerivedStateFromProps()
  • Child 组件: shouldComponentUpdate()
  • Child 组件: render()
  • Child 组件: getSnapshotBeforeUpdate()
  • Parent 组件: getSnapshotBeforeUpdate()
  • Child 组件: componentDidUpdate()
  • Parent 组件: componentDidUpdate()
四、卸载子组件

点击父组件中的 [卸载 / 挂载子组件] 按钮,则界面上子组件会消失,控制台的打印顺序为:

  • Parent 组件: getDerivedStateFromProps()
  • Parent 组件: shouldComponentUpdate()
  • Parent 组件: render()
  • Parent 组件: getSnapshotBeforeUpdate()
  • Child 组件: componentWillUnmount()
  • Parent 组件: componentDidUpdate()
五、重新挂载子组件

再次点击父组件中的 [卸载 / 挂载子组件] 按钮,则界面上子组件会重新渲染出来,控制台的打印顺序为:

  • Parent 组件: getDerivedStateFromProps()
  • Parent 组件: shouldComponentUpdate()
  • Parent 组件: render()
  • Child 组件: constructor()
  • Child 组件: getDerivedStateFromProps()
  • Child 组件: render()
  • Parent 组件: getSnapshotBeforeUpdate()
  • Child 组件: componentDidMount()
  • Parent 组件: componentDidUpdate()

父子组件生命周期执行顺序总结:

  • 当子组件自身状态改变时,不会对父组件产生副作用的情况下,父组件不会进行更新,即不会触发父组件的生命周期

  • 当父组件中状态发生变化(包括子组件的挂载以及卸载)时,会触发自身对应的生命周期以及子组件的更新

    • render 以及 render 之前的生命周期,则 父组件先执行
    • render 以及 render之后的声明周期,则子组件先执行,并且是与父组件交替执行

    当子组件进行卸载时,只会执行自身的 componentWillUnmount 生命周期,不会再触发别的生命周期

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/378569.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【论文阅读】-姿态识别

记录论文阅读&#xff0c;希望能了解我方向的邻域前沿吧 粗读 第一篇 ATTEND TO WHO YOU ARE: SUPERVISING SELF-ATTENTION FOR KEYPOINT DETECTION AND INSTANCE-AWARE ASSOCIATION 翻译&#xff1a;https://editor.csdn.net/md?not_checkout1&spm1001.2014.3001.5352…

设计模式系列 - 代理模式及动态代理详解

定义 为其他对象提供一种代理以控制对这个对象的访问。在某些情况下&#xff0c;一个对象不适合或者不能直接引用另一个对象&#xff0c;而代理对象可以在客户端和目标对象之间起到中介的作用。 结构 抽象角色&#xff1a;通过接口或抽象类声明真实角色实现的业务方法。 代…

系列七、索引

一、索引概述 1.1、概述 索引&#xff08;index&#xff09;是帮助MySQL高效获取数据的数据结构(有序)。在数据之外&#xff0c;数据库系统还维护着满足特定查找算法的数据结构&#xff0c;这些数据结构以某种方式引用&#xff08;指向&#xff09;数据&#xff0c; 这样就可以…

NCRE计算机等级考试Python真题(七)

第七套试题1、在面向对象方法中&#xff0c;一个对象请求另一对象为其服务的方式是通过发送___________。A.命令B.口令C消息D.调用语句正确答案&#xff1a; C2、下面不属于软件需求分析阶段主要工作的是___________。A.需求评审B.需求获取C.需求变更申请D.需求分析正确答案&am…

MySQL 索引失效场景

1&#xff0c;前言 索引主要是为了提高表的查询速率&#xff0c;但在某些情况下&#xff0c;索引也会失效的情况。 2&#xff0c;失效场景 2.1 最左前缀法则 查询从索引最左列开始&#xff0c;如果跳过索引中的age列&#xff0c;那么age后面字段的索引都将失效&#xff0c;…

接电话蓝牙耳机什么牌子好?语音质量好的蓝牙耳机

越来越多的人都离不开蓝牙耳机了&#xff0c;因为它用起来是真的太方便了&#xff0c;相信后续智能手机也会更多地取消3.5耳机孔&#xff0c;届时蓝牙耳机将会更加普遍&#xff0c;甚至是人手一部&#xff0c;下面分享几款语音质量好的蓝牙耳机。 第一款&#xff1a;南卡小音舱…

云端IDE系列教程7:解决 WeTTY 在 Ubuntu 非 root 用户不能运行的问题

原文作者&#xff1a;行云创新技术总监 邓冰寒 概述 上一期在使用官方容器镜像快速成功地在 TitanIDE 运行起来了 WeTTY&#xff0c;但是不适合开发人员使用&#xff0c;而我自己编译构建出来的容器镜像无法直接运行指定的应用&#xff08;/bin/bash 或 /bin/zsh&#xff09;&…

HBase基础知识

1、HBase特点 1&#xff09;海量存储 Hbase适合存储PB级别的海量数据&#xff0c;在PB级别的数据以及采用廉价PC存储的情况下&#xff0c;能在几十到百毫秒内返回数据。这与Hbase的极易扩展性息息相关。正式因为Hbase良好的扩展性&#xff0c;才为海量数据的存储提供了便利。…

收下这份十万商家称赞的开店攻略,带你发家致富!

理想与现实之间的距离&#xff0c;大概就是开店吧&#xff01;总觉得自己投点钱&#xff0c;一两年回本&#xff0c;后面每月轻松赚几万、几十万&#xff1b;结果却发现房租太贵、人工太贵、自己什么都不懂&#xff0c;然后随波逐流的没有特色。其实&#xff0c;细心的朋友会发…

经常打电话的人用什么蓝牙耳机好?通话功能比较好的蓝牙耳机

无线耳机市场发生了翻天覆地的变化&#xff0c;开始越来越频繁地出现不一样的功能。现在&#xff0c;选择不于 Apple&#xff0c;还包括一大堆可用的耳机&#xff0c;下面就来看看以下通话功能好的蓝牙耳机。 第一款&#xff1a;南卡小音舱蓝牙耳机 售价&#xff1a;299元 推…

【数电基础】——逻辑代数运算

目录 1.概念 1.基本逻辑概念 2.基本逻辑电路&#xff08;与或非&#xff09; 逻辑与运算 与门电路&#xff1a; 逻辑或运算 或门电路&#xff1a; ​逻辑非运算&#xff08;逻辑反&#xff09; 非门电路​编辑 3.复合逻辑电路&#xff08;运算&#xff09; 与非逻辑…

初探推荐系统-01

文章目录一、什么是推荐系统是什么为什么长尾理论怎么做二、相似度算法杰卡德相似系数余弦相似度三、基于内容的推荐算法如何获取到用户喜欢的物品如何确定物品的特征四、推荐算法实验方法评测指标推荐效果实验方法1、离线实验2、用户调查3、在线实验评测指标1、预测准确度评分…

【组织架构】中国国家铁路集团有限公司

1 公司简介 中国国家铁路集团有限公司&#xff08;简称“中国铁路”&#xff09;是经国务院批准、依据《中华人民共和国公司法》设立、由中央管理的国有独资公司。经国务院批准&#xff0c;公司为国家授权投资机构和国家控股公司。公司注册资本为17395亿元&#xff0c;由财政部…

CAD拉伸后标注尺寸不变?快来看看是不是这个原因!

CAD拉CAD拉伸后标注尺寸不变&#xff1f;快来看看是不是这个原因&#xff01;CAD拉命令作为常用的图形编辑命令之一&#xff0c;有些设计师在使用过程中发现&#xff0c;CAD拉伸后标注尺寸没有变化&#xff0c;这是什么情况&#xff1f;本节课程小编就以浩辰CAD软件为例来给大家…

心系区域发展,高德用一体化出行服务平台“聚”力区域未来

交通&#xff0c;是城市的血脉。通过对人、资源、产业的连接&#xff0c;交通建设往往是城市和区域经济发展的前提。不过&#xff0c;在度过了“要想富&#xff0c;先修路”的初级建设阶段后&#xff0c;交通产业内部也出现了挑战&#xff0c;诸如城市秩序、发展成本、用户使用…

《爆肝整理》保姆级系列教程python接口自动化(二十五)--unittest断言——下(详解)

简介 本篇还是回归到我们最初始的话题&#xff0c;想必大家都忘记了&#xff0c;没关系看这里&#xff1a; 没错最初的话题就是登录&#xff0c;由于博客园的登录机制改变了&#xff0c;本篇以我找到的开源免费的登录API为案例&#xff0c;结合 unittest 框架写 2 个用例。同样…

想学计算机,应该学什么专业?

我们在考虑想学计算机&#xff0c;应该学什么专业&#xff1f;这个问题的时候&#xff0c;每个人都应该结合自己的兴趣来确定。有的喜欢编程、有的喜欢设计、有的喜欢做产品跟人打交道……自己有兴趣再加上自己的努力&#xff0c;掌握好专业技能&#xff0c;就一定能进入高薪的…

【Spark分布式内存计算框架——Spark Streaming】7. Kafka集成方式

集成方式 Spark Streaming与Kafka集成&#xff0c;有两套API&#xff0c;原因在于Kafka Consumer API有两套&#xff0c; 文档&#xff1a;http://spark.apache.org/docs/2.4.5/streaming-kafka-integration.html。 方式一&#xff1a;Kafka 0.8.x版本 老的Old Kafka Consum…

MyBatis学习笔记(八) —— 字段名和属性不一致的情况下,如何处理映射关系

EmpMapper.java /** * 根据id查询员工信息 * param empId * return */ Emp getEmpByEmpId(Param("empId") Integer empId);EmpMapper.xml <?xml version"1.0" encoding"UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//D…

Android仿QQ未读消息拖拽粘性效果

效果图原理分析首先是在指定某个位置画一个圆出来&#xff0c;手指按到这个圆的时候再绘制一个可以根据手指位置移动的圆&#xff0c;随着手指的移动两个圆逐渐分离&#xff0c;分离的过程中两圆中间出现连接带&#xff0c;随着两圆圆心距的增大&#xff0c;半径也是根据某一比…