草庐IT

React生命周期详解

学学学无止境 2023-04-11 原文

前言

        react生命周期在各个阶段的执行顺序是有所差异的,接下来我们将详细解析react在各个阶段生命周期的执行顺序,以及各个生命周期在组件中充当的作用(以下生命周期指的是react16之后的版本)

 

组件挂载时

当组件实例被创建并插入DOM时,其生命周期调用顺序如下:

  • constructor()
  • static getDerivedStateFromProps()
  • render()
  • componentDidMount()

组件更新时

当组件的props或state发生变化时会触发更新。组件更新的生命周期调用顺序如下:

  • static getDerivedStateFromProps()
  • shouldComponentUpdate()
  • render()
  • getSnapshotBeforeUpdate()
  • componentDidUpdate()

组件卸载时

当组件从DOM中移除时会调用如下方法

  • componentWillUnmount()

错误处理

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

  • static getDerivedStateFromProps()
  • componentDidCatch()

常用生命周期

1.render()

render()方法时class组件中唯一必须要实现的方法,当render被调用时,它会检查this.props和this.state的变化并返回下类型:

  • React元素:通常通过jsx创建。比如,<div />会被渲染为DOM节点,<MyComponent />会被React渲染成为自定义组件,武林是<div/>还是<MyComponent/>均为React元素。
  • 数组或fragments:使得render方法可以返回多个元素,想了解更加详细,参见fragments文档:Fragments – React
  • Portals:可以渲染子节点到不同的DOM子树中,
  • 字符串或者数值类型:他们在DOM中会被渲染为文本节点
  • 布尔类型或null:什么都不渲染(主要用于支持返回test &&<Child />默认,其中test为布尔类型)

render()函数应该为纯函数,这意味着不在修改组件state情况下,每次调用时都返回相同的结果,并且它不会直接与浏览器交互。

如需与浏览器进行交互,请在componentDidMount()或者其他生命周期方法中执行你的操作,保持render()为纯函数,可以使组件更容易思考

注意:shouldComponentUpdate()返回false,则不会调用render()

 2.constructor()构造函数

constructor(props)

如果不初始化state或不进行方法绑定,则不需要为React组件实现构造函数

在React组件挂在之前,会调用它的构造函数。在为React.Component子类实现构造函数时,应在其他语句之前调用super(props)。斗则,this.props在构造函数中可能会出现未定义的bug

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

  • 通过给this.state赋值对象来初始化内部state
  • 为事件处理函数绑定实例

constructor()函数中不要调用setState()方法,如果你的组件需要使用内部state,请直接在构造函数中为this.state赋值初始化state

constructor(props) {
  super(props);
  // 不要在这里调用 this.setState()
  this.state = { counter: 0 };
  this.handleClick = this.handleClick.bind(this);
}

只能在构造函数中直接为this.state赋值,如需要在其他方法中赋值,应该使用this.setState()替代

要避免在构造函数中引入任何副作用或订阅,如遇场景,请将对应的擦欧总放置在componentDidMount中

注意:避免将props的值复制给state!这是一个常见的错误:

constructor(props) {
 super(props);
 // 不要这样做
 this.state = { color: props.color };
}

如此做毫无必要(你可以直接使用this.props.color),同事还产生了bug(更新prop中的color时,并不影响state)

 3.componentDidMount()

componentDidMount()会在组件挂在(插入DOM树中)立即调用。依赖DOM节点的初始化应该放在这里。如需通过网络请求获取数据,此处实例化请求的好地方。

这个方法是比较适合添加订阅的方法,如果添加了订阅,请不要忘记在componentWillUnmount中进行取消

你可以在componentDidMount()里直接调用setState()。它将触发额外渲染,但此渲染会发生在浏览器更新屏幕之前。如此保证了即使在render()两次调用的情况下,用户也不会看到中间状态。

4.componentDidUpdate()

componentDidUpdate(prevProps,prevState,snapshot)//prevProps表示之前的prop

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

当组件更新后,可以在此处对DOM进行操作。如果你对更新前后的prop进行了比较,也可以渲染在此处进行网络请求(例如,当props未发生变化时,则不会执行网络请求)

componentDidUpdate(prevProps){
    //典型用法(不要忘记比较props)
    if(this.props.userId!==prevProps.userId){
        this.fetchDate(this.props.userId)
    }
}

如果组件实现了getSnapshotBeforeUpdate()生命周期(不常用),则它的返回值将作为componentDidUpdate()的第三个参数“snapshot”参数传递,否则此参数将为undefined。

注意:如果shouldComponentUpdate()返回值未false,则不会调用componentDidUpdate()

 5.componentWillUnmount()

componentWillUnmount() 会在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,例如,清除 timer,取消网络请求或清除在 componentDidMount() 中创建的订阅等。

componentWillUnmount() 中不应调用 setState(),因为该组件将永远不会重新渲染。组件实例卸载后,将永远不会再挂载它。

不常用的生命周期方法

1.shouldComponentUpdate()

shouldComponentUpdate(nextProps,nextState)

根据shouldComponentUpdate()的返回值,判断React组件的输出时否手当前state或props更改的影响,默认行为时state每次发生改变组件都会渲染,大部分情况下,你应该遵循默认行为。

当props或者state发生改变时,shouldComponentUpdate()会在渲染执行之前被调用。默认返回值未true,首次渲染或使用forceUpdate()时不会调用该方法。

此方法仅作为性能优化方式而存在,不要企图依靠此方法来阻止渲染,因为这可能会尝试bug。你应该考虑使用内置PrueComponent组件,而不是手动编写shouldComponentUpdate(),PureComponent会对props和state进行浅层比较,并减少跳过必要更新的可能性。

2.static getDerivedStateFromProps()

3.getSnapshotBeforeUpdate()

4.static getDerivedStateFromError()

5.componentDidCatch()

其他API

不同于上述声明周期方法(React主动调用),以下方法你可以在组件中调用的方法

只有两个方法:setState()forceUpdate()

1.setState()

setState(updater,[callback])

setState()将对组件色state的更改排入队列,并通知React需要使用更新后的state重新渲染此组件及其子组件,这是用于更新用户界面以及响应事件处理器和处理服务器数据的主要方式。

将setState()视为请求而不是立即更新组件的命令,为了更好的感知性能,React会延迟调用,通过一次传递更新多个组件,React并不会保证state的变更会立即生效(及setState()对state状态的更新不是同步的

setState()并不总是立即更新组件,他会批量推迟更新,这使得在调用setState()后立即读取this.state成为隐患。为了消除隐患,请使用componentDidUpdate或者setState的回调函数

setState(updater,callback),这两种方式都可以保证在应用更新后触发。如需基于值卡的state来设置当前的state,请阅读关于参数updater函数的内容。

this.setState((props,state)=>{
    return {counter:state.counter+props.step};
})

updater函数中接收的state和props都保证为最新。updater的返回值会与state进行浅合并

setState()的第二个参数为可选的回调函数,它将在setState完成合并并重新渲染组件后执行。通常,我们建议使用componentDidUpdate()来代替此方法

setState()的第一个参数除了接收函数外,还可以接收对象类型

setState(stateChange[, callback])

setChange会将传入的队形浅层合并到新的state。例如,调整购物车商品数:

this.setState({quantity:2})

这种形式的setState()也是异步的,并且在统一周期会对多个setState进行批处理。例如,如果在同意周期内多次设置商品数量增加,则相当于:

Object.assign(
  previousState,
  {quantity: state.quantity + 1},
  {quantity: state.quantity + 1},
  ...
)

后调用的setState()将覆盖统一周期内先调用setState的值,因此商品数量仅增加一次,如果后续状态取决于当前状态,我们建议使用updater函数的形式代替:

this.setState((state)=>{
    return {quantity:state.quantity+1}
})

2.forceUpdate()

component.forceUpdate(callback)

默认情况下,当组件的state或props发生改变时,组件将重新渲染。如果render()方法依赖于其他数据,则可以调用forceUpdate()强制让组件重新渲染。

调用forceUpdate()将致使组件调用render()方法,此操作会跳过该组件的shouldComponentUpdate(),但其子组件会触发正常的声明周期方法。

通常你应该避免使用 forceUpdate(),尽量在 render() 中使用 this.props 和 this.state

有关React生命周期详解的更多相关文章

  1. ruby-on-rails - Ruby 长时间运行的进程对队列事件使用react - 2

    我有一个将某些事件写入队列的Rails3应用。现在我想在服务器上创建一个服务,每x秒轮询一次队列,并按计划执行其他任务。除了创建ruby​​脚本并通过cron作业运行它之外,还有其他稳定的替代方案吗? 最佳答案 尽管启动基于Rails的持久任务是一种选择,但您可能希望查看更有序的系统,例如delayed_job或Starling管理您的工作量。我建议不要在cron中运行某些东西,因为启动整个Rails堆栈的开销可能很大。每隔几秒运行一次它是不切实际的,因为Rails上的启动时间通常为5-15秒,具体取决于您的硬件。不过,每天这样做几

  2. 物联网MQTT协议详解 - 2

    一、什么是MQTT协议MessageQueuingTelemetryTransport:消息队列遥测传输协议。是一种基于客户端-服务端的发布/订阅模式。与HTTP一样,基于TCP/IP协议之上的通讯协议,提供有序、无损、双向连接,由IBM(蓝色巨人)发布。原理:(1)MQTT协议身份和消息格式有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。MQTT传输的消息分为:主题(Topic)和负载(payload)两部分Topic,可以理解为消息的类型,订阅者订阅(Su

  3. Tcl脚本入门笔记详解(一) - 2

    TCL脚本语言简介•TCL(ToolCommandLanguage)是一种解释执行的脚本语言(ScriptingLanguage),它提供了通用的编程能力:支持变量、过程和控制结构;同时TCL还拥有一个功能强大的固有的核心命令集。TCL经常被用于快速原型开发,脚本编程,GUI和测试等方面。•实际上包含了两个部分:一个语言和一个库。首先,Tcl是一种简单的脚本语言,主要使用于发布命令给一些互交程序如文本编辑器、调试器和shell。由于TCL的解释器是用C\C++语言的过程库实现的,因此在某种意义上我们又可以把TCL看作C库,这个库中有丰富的用于扩展TCL命令的C\C++过程和函数,所以,Tcl是

  4. ruby - 单个 EventMachine react 器中的多个服务器 - 2

    是否可以在单个事件机器中运行多个服务器?我的意思是,多个服务可以由一个客户端连接同时使用。例如,登录服务器对用户进行身份验证,然后用户可以同时使用聊天室和简单的游戏,例如带有单个客户端套接字的跳棋?或者我是否需要为每个服务使用多个事件机器react器? 最佳答案 我试过了,它正在工作:#!/usr/bin/envrubyrequire'eventmachine'moduleEchoServerdefpost_initputs"--someoneconnectedtotheechoserver!"enddefreceive_datad

  5. 【详解】Docker安装Elasticsearch7.16.1集群 - 2

    开门见山|拉取镜像dockerpullelasticsearch:7.16.1|配置存放的目录#存放配置文件的文件夹mkdir-p/opt/docker/elasticsearch/node-1/config#存放数据的文件夹mkdir-p/opt/docker/elasticsearch/node-1/data#存放运行日志的文件夹mkdir-p/opt/docker/elasticsearch/node-1/log#存放IK分词插件的文件夹mkdir-p/opt/docker/elasticsearch/node-1/plugins若你使用了moba,直接右键新建即可如上图所示依次类推创建

  6. 【Elasticsearch基础】Elasticsearch索引、文档以及映射操作详解 - 2

    文章目录概念索引相关操作创建索引更新副本查看索引删除索引索引的打开与关闭收缩索引索引别名查询索引别名文档相关操作新建文档查询文档更新文档删除文档映射相关操作查询文档映射创建静态映射创建索引并添加映射概念es中有三个概念要清楚,分别为索引、映射和文档(不用死记硬背,大概有个印象就可以)索引可理解为MySQL数据库;映射可理解为MySQL的表结构;文档可理解为MySQL表中的每行数据静态映射和动态映射上面已经介绍了,映射可理解为MySQL的表结构,在MySQL中,向表中插入数据是需要先创建表结构的;但在es中不必这样,可以直接插入文档,es可以根据插入的文档(数据),动态的创建映射(表结构),这就

  7. 最强Http缓存策略之强缓存和协商缓存的详解与应用实例 - 2

    HTTP缓存是指浏览器或者代理服务器将已经请求过的资源保存到本地,以便下次请求时能够直接从缓存中获取资源,从而减少网络请求次数,提高网页的加载速度和用户体验。缓存分为强缓存和协商缓存两种模式。一.强缓存强缓存是指浏览器直接从本地缓存中获取资源,而不需要向web服务器发出网络请求。这是因为浏览器在第一次请求资源时,服务器会在响应头中添加相关缓存的响应头,以表明该资源的缓存策略。常见的强缓存响应头如下所述:Cache-ControlCache-Control响应头是用于控制强制缓存和协商缓存的缓存策略。该响应头中的指令如下:max-age:指定该资源在本地缓存的最长有效时间,以秒为单位。例如:Ca

  8. IDEA 2022 创建 Spring Boot 项目详解 - 2

    如何用IDEA2022创建并初始化一个SpringBoot项目?目录如何用IDEA2022创建并初始化一个SpringBoot项目?0. 环境说明1.  创建SpringBoot项目 2.编写初始化代码0. 环境说明IDEA2022.3.1JDK1.8SpringBoot1.  创建SpringBoot项目        打开IDEA,选择NewProject创建项目。        填写项目名称、项目构建方式、jdk版本,按需要修改项目文件路径等信息。        选择springboot版本以及需要的包,此处只选择了springweb。        此处需特别注意,若你使用的是jdk1

  9. 详解Unity中的粒子系统Particle System (二) - 2

    前言上一篇我们简要讲述了粒子系统是什么,如何添加,以及基本模块的介绍,以及对于曲线和颜色编辑器的讲解。从本篇开始,我们将按照模块结构讲解下去,本篇主要讲粒子系统的主模块,该模块主要是控制粒子的初始状态和全局属性的,以下是关于该模块的介绍,请大家指正。目录前言本系列提要一、粒子系统主模块1.阅读前注意事项2.参考图3.参数讲解DurationLoopingPrewarmStartDelayStartLifetimeStartSpeed3DStartSizeStartSize3DStartRotationStartRotationFlipRotationStartColorGravityModif

  10. VMware虚拟机与本地主机进行磁盘共享(详解) - 2

    VMware虚拟机与本地主机进行磁盘共享前提虚拟机版本为Windows10(专业版,不是可能有问题)本地主机为家庭版或学生版(此版本会有问题,但有替代方式)最好是专业版VMware操作1.关闭防火墙,全部关闭。2.打开电脑属性3.点击共享-》高级共享-》权限4.如果没有everyone,就添加权限选择完全控制,然后应用确定。5.打开cmd输入lusrmgr.msc(只有专业版可以打开)如果不是专业版,可以跳过这一步。点击用户-》administrator密码要复杂密码,否则不行。推荐admaiN@1234类型的密码。设置完密码,点击属性,将禁用解开。6.如果虚拟机的windows不是专业版,可

随机推荐