【React】React组件生命周期以及触发顺序(部分与vue做比较)

最近在学习React,发现其中的生命周期跟Vue有一些共同点,但也有比较明显的区别,并且执行顺序也值得讨论一下,于是总结了一些资料在这里,作为学习记录。

v17.0.1后生命周期图片

在这里插入图片描述

初始化阶段

由ReactDOM.render()触发 —— 初次渲染

  1. constructor() —— 类组件中的构造函数
  2. static getDerivedStateFromProps(props, state) 从props得到一个派生的状态【新增】
  3. render() —— 挂载组件
  4. componentDidMount() —— 组件挂载完成 比较常用
    总结:
    constructor 对标 Vue中的beforeCreate/created
    componentDidMount 对标 Vue中的 Mounted
    在一个完整的生命周期中,constructor 与 componentDidMount 只会执行一次。
    在一个完整的生命周期中,render会执行多次
    注意:
    在React中,我们在componentDidMount 中发请求,绝对不在constructor 中发请求。

更新阶段

由组件内部this.setSate()或父组件重新render触发或强制更新forceUpdate()

  1. getDerivedStateFromProps() —— 从props得到一个派生的状态 【新增】
  2. shouldComponentUpdate() —— 组件是否应该被更新(默认返回true)
  3. render() —— 挂载组件
  4. getSnapshotBeforeUpdate() —— 在更新之前获取快照【新增】
  5. componentDidUpdate(prevProps, prevState, snapshotValue) —— 组件完成更新。
    总结:
    触发组件更新的方式(常用),两种:
    1. 💥props 值的改变
    2. 💥setState() 改变state
      更新阶段触发的钩子函数,有两个
      1. render
      2. componentDidUpdate
        render与componentsDidUpdate 都可以拿到更新后的值。
        render与componentsDidUpdate 中都不能调用setState ,会造成死循环。
        注意:
        不论DOM中有没有使用数据,钩子函数都会被触发。(与vue不同)
        react中的更新,指的是数据更新,而非视图更新。(与vue不同)

卸载组件

由ReactDOM.unmountComponentAtNode()触发

  1. componentWillUnmount() —— 组件即将卸载

重要的勾子

  1. render:初始化渲染或更新渲染调用
  2. componentDidMount:开启监听, 发送ajax请求
  3. componentWillUnmount:做一些收尾工作, 如: 清理定时器

即将废弃的勾子

  1. componentWillMount
  2. componentWillReceiveProps
  3. componentWillUpdate
    现在使用会出现警告,下一个大版本需要加上UNSAFE_前缀才能使用,以后可能会被彻底废弃,不建议使用。

代码案例

效果
在这里插入图片描述
代码展示

父组件: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之后的生命周期,子组件先执行,并且与父组件交替执行
  • 当子组件进行卸载时,只会执行自身的 componentWillUnmount 生命周期,不会再触发别的生命周期

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Axure】元件库和母版、常见的原型规范、静态原型页面制作

添加现有元件库 点击元件库——载入 当然也可以创建元件库&#xff0c;自己画自己保存 建立京东秒杀母版 静态原型页面的制作 框架 选择以iphone8的界面大小为例&#xff0c;顶部状态栏高度为20 左侧类似于标尺&#xff0c;因为图标、文字离最左侧的间距是不一样的 信…

Nat. Commun. | 大规模高分辨单光子成像

本文由论文作者团队(课题组)投稿 单光子雪崩二极管(Single Photon Avalanche Diode,简称SPAD)阵列因其极佳的单光子灵敏度而受到广泛关注,已广泛应用于量子通信与计算、荧光寿命成像、时间飞行成像等各个领域。与同样具有较高灵敏度的EMCCD和sCMOS相比,SPAD阵列能够在极…

车载ADB环境搭建

ADB是什么 ADB&#xff0c;即 Android Debug Bridge 是一种允许模拟器或已连接的 Android 设备进行通信的命令行工具&#xff0c;它可为各种设备操作提供便利&#xff0c;如安装和调试应用&#xff0c;并提供对 Unix shell&#xff08;可用来在模拟器或连接的设备上运行各种命…

SimpleCG动画示例--汉诺塔动画演示

前言 SimpleCG的使用方法在前面已经介绍了许多&#xff0c;有兴趣的同学如果有去动手&#xff0c;制作一些简单动画应该没多大问题的。所以这次我们来演示一下简单动画。我们刚学习C语言的递归函数时&#xff0c;有一个经典例子相信很多同学都写过&#xff0c;那就是汉诺塔。那…

【C++】多线程的学习笔记——白话文版(bushi

目录 为什么要使用多线程 例子 代码 结果 首先要先学的库——thread库 thread的简介 thread的具体使用方法 基本变量的定义 注意&#xff08;小重点&#xff09; join函数的解读&#xff08;重点&#xff09; detach函数的解读 注意 关于vector和thread是联合使用 …

【DLoopDetector(C++)】DBow2词袋模型loop close学习

0.前言 最近读了两篇论文&#xff0c;论文作者开源了一种基于词袋模型DBoW2库的DLoopDetector算法&#xff0c;自己运行demo测试一下 对应论文介绍&#xff1a;Bags of Binary Words for Fast Place Recognition in Image Sequences 开源项目Github地址&#xff1a;https://gi…

P1541 [NOIP2010 提高组] 乌龟棋(4维背包问题)

[NOIP2010 提高组] 乌龟棋 题目背景 小明过生日的时候&#xff0c;爸爸送给他一副乌龟棋当作礼物。 题目描述 乌龟棋的棋盘是一行 N N N 个格子&#xff0c;每个格子上一个分数&#xff08;非负整数&#xff09;。棋盘第 1 1 1 格是唯一的起点&#xff0c;第 N N N 格是…

C++代码示例:排列数简单生成工具

文章目录 前言代码仓库内容代码&#xff08;有详细注释&#xff09;编译和运行命令结果总结参考资料作者的话 前言 C代码示例&#xff1a;排列数简单生成工具。 代码仓库 yezhening/Programming-examples: 编程实例 (github.com)Programming-examples: 编程实例 (gitee.com) …

GEO生信数据挖掘(四)数据清洗(离群值处理、低表达基因、归一化、log2处理)

检索到目标数据集后&#xff0c;开始数据挖掘&#xff0c;本文以阿尔兹海默症数据集GSE1297为例 目录 离群值处理 删除 低表达基因 函数归一化&#xff0c;矫正差异 数据标准化—log2处理 完整代码 上节围绕着探针ID和基因名称做了一些清洗工作&#xff0c;还做了重复值检查…

牛客网_HJ2_计算某字符出现次数

HJ2_计算某字符出现次数 原题思路代码运行截图收获 原题 HJ2_计算某字符出现次数 思路 把输入的字符串和字符都变成大写或小写&#xff0c;然后逐一计数 代码 #include <cctype> #include <iostream> #include <string> #include <algorithm> usi…

java多线程相关介绍

1. 线程的创建和启动 在 Java 中创建线程有两种方式。一种是继承 Thread 类并重写其中的 run() 方法&#xff0c;另一种是实现 Runnable 接口并重写其中的 run() 方法。创建完线程对象后&#xff0c;调用 start() 方法可以启动线程。 2. 线程的状态 Java 的线程在不同阶段会处于…

C++八股

1、简述一下C中的多态 在面向对象中&#xff0c;多态是指通过基类的指针或引用&#xff0c;在运行时动态调用实际绑定对象函数的行为&#xff0c;与之相对应的编译时绑定函数称为静态绑定。 静态多态 静态多态是编译器在编译期间完成的&#xff0c;编译器会根据实参类型来选择…

自动驾驶技术:现状与未来

自动驾驶技术&#xff1a;现状与未来 文章目录 引言自动驾驶技术的现状自动驾驶技术的挑战自动驾驶技术的未来结论结论 2023星火培训【专项营】Apollo开发者社区布道师倾力打造&#xff0c;包含PnC、新感知等的全新专项课程上线了。理论与实践相结合&#xff0c;全新的PnC培训不…

Vim学习笔记

博客主页&#xff1a;https://tomcat.blog.csdn.net 博主昵称&#xff1a;农民工老王 主要领域&#xff1a;Java、Linux、K8S 期待大家的关注&#x1f496;点赞&#x1f44d;收藏⭐留言&#x1f4ac; 目录 模式介绍指令概览启动退出移动光标插入删除复制替换撤销搜索信息设置外…

Redis缓存穿透、击穿和雪崩

面试高频 服务的高可用问题&#xff01; 在这里我们不会详细的区分析解决方案的底层&#xff01; Redis缓存概念 Redis缓存的使用&#xff0c;极大的提升了应用程序的性能和效率&#xff0c;特别是数据查询方面。但同时&#xff0c;它也带来了一些问题。其中&#xff0c;最要…

Linux shell编程学习笔记4:修改命令行提示符格式(内容和颜色)

一、命令行提示符格式内容因shell类型而异 Linux终端命令行提示符内容格式则因shell的类型而异&#xff0c;例如CoreLinux默认的shell是sh&#xff0c;其命令行提示符为黑底白字&#xff0c;内容为&#xff1a; tcbox:/$ 其中&#xff0c;tc为当前用户名&#xff0c;box为主机…

【JVM】并发可达性分析-三色标记算法

欢迎访问&#x1f44b;zjyun.cc 可达性分析 为了验证堆中的对象是否为可回收对象&#xff08;Garbage&#xff09;标记上的对象&#xff0c;即是存活的对象&#xff0c;不会被垃圾回收器回收&#xff0c;没有标记的对象会被垃圾回收器回收&#xff0c;在标记的过程中需要stop…

LabVIEW开发光学相干断层扫描系统

LabVIEW开发光学相干断层扫描系统 癌症是一种以异常或受损细胞无法控制生长为特征的疾病&#xff0c;是世界上导致死亡的主要原因之一。以前的研究人员已经表明&#xff0c;患病时组织力学会发生变化。能够同时量化和可视化组织力学和细胞行为有可能弥合我们对这两种癌症驱动特…

Oracle - 多区间按权重取值逻辑

啰嗦: 其实很早就遇到过类似问题&#xff0c;也设想过&#xff0c;不过一致没实际业务需求&#xff0c;也就耽搁了&#xff1b;最近有业务提到了&#xff0c;和同事讨论&#xff0c;各有想法&#xff0c;所以先把逻辑整理出来&#xff0c;希望有更好更优的解决方案&#xff1b;…

C++简单实现AVL树

目录 一、AVL树的概念 二、AVL树的性质 三、AVL树节点的定义 四、AVL树的插入 4.1 parent的平衡因子为0 4.2 parent的平衡因子为1或-1 4.3 parent的平衡因子为2或-2 4.3.1 左单旋 4.3.2 右单旋 4.3.3 先左单旋再右单旋 4.3.4 先右单旋再左单旋 4.4 插入节点完整代码…