您当前的位置:首页 > 电脑百科 > 程序开发 > 编程百科

React生命周期详解(新版)

时间:2023-06-15 12:56:28  来源:  作者:尚硅谷教育

本篇文章带大家看看React新版生命周期带来了哪些变化。

React16.4版本之后使用了新的生命周期,它使用了一些新的生命周期钩子(getDerivedStateFromProps、getSnapshotBeforeUpdate),并且即将废弃老版的3个生命周期钩子(componentWillMount、componentWillReceiveProps、componentWillUpdate)。

一、新版生命周期

如图所示,我们可以看到,在组件第一次挂载时会经历:

构造器(constructor)=》修改state属性(getDerivedStateFromProps)=》组件挂载渲染(render)=》组件挂载完成(componentDidMount)

组件内部状态更新:

更新state属性(getDerivedStateFromProps)=》判断组件是否更新(shouldComponentUpdate)=》组件更新渲染(render)=》(getSnapshotBeforeUpdate)=》组件更新完成(componentDidUpdate)

组件卸载时执行:

组件销毁(componentWillUnmount)

注意:

新版本使用了getDerivedStateFromProps代替了componentWillMount、componentWillReceiveProps、componentWillUpdate三个钩子函数,如果在旧版本中使用将会警告提示。它必须要return一个null或者对象,并且会影响初始化的值以及修改的值。

getSnapshotBeforeUpdate钩子必须与componentDidUpdate搭配使用否则会报错。在旧版本中使用将会警告提示。必须要return一个null或者任何值,它将在最近一次渲染输出(提交到DOM节点)之前调用。

二、生命周期新增函数详解

static getDerivedStateFromProps(getDSFP)

首先这个新的方法是一个静态方法,在这里不能调用this,也就是一个纯函数。它传了两个参数,一个是新的nextProps ,一个是之前的prevState,所以只能通过prevState而不是prevProps来做对比,它保证了state和props之间的简单关系以及不需要处理第一次渲染时prevProps为空的情况。也基于以上两点,将原本componentWillReceiveProps里进行的更新工作分成两步来处理,一步是setState状态变化,更新 state在getDerivedStateFromProps里直接处理。

旧的React中componentWillReceiveProps方法是用来判断前后两个props是否相同,如果不同,则将新的props更新到相应的state上去。在这个过程中我们实际上是可以访问到当前props的,这样我们可能会对this.props做一些奇奇怪怪的操作,很可能会破坏state数据的单一数据源,导致组件状态变得不可预测。

而在getDerivedStateFromProps中禁止了组件去访问 this.props,强制让开发者去比较nextProps与prevState中的值,以确保当开发者用到getDerivedStateFromProps这个生命周期函数时,就是在根据当前的props来更新组件的state,而不是去访问this.props并做其他一些让组件自身状态变得更加不可预测的事情。

getSnapshotBeforeUpdate

在React开启异步渲染模式后,在执行函数时读到的DOM元素状态并不一定和渲染时相同,这就导致在componentDidUpdate中使用的DOM元素状态是不安全的(不一定是最新的),因为这时的值很有可能已经失效了。

与componentWillMount不同的是,getSnapshotBeforeUpdate会在最终确定的render执行之前执行,也就是能保证其获取到的元素状态与componentDidUpdate中获取到的元素状态相同。

这个方法并不常用,但它可能出现在UI处理中,如需要以特殊方式处理滚动位置的聊天线程等。并且会返回snapshot的值或null。例如:

class ScrollingList extends React.Component {

constructor(props) {

super(props);

this.listRef = React.createRef();

}

getSnapshotBeforeUpdate(prevProps, prevState) {

// 我们是否在 list 中添加新的 items ?

// 捕获滚动位置以便我们稍后调整滚动位置。

if (prevProps.list.length < this.props.list.length) {

const list = this.listRef.current;

return list.scrollHeight - list.scrollTop;

}

return null;

}

componentDidUpdate(prevProps, prevState, snapshot) {

// 如果我们 snapshot 有值,说明我们刚刚添加了新的 items,

// 调整滚动位置使得这些新 items 不会将旧的 items 推出视图。

//(这里的 snapshot 是 getSnapshotBeforeUpdate 的返回值)

if (snapshot !== null) {

const list = this.listRef.current;

list.scrollTop = list.scrollHeight - snapshot;

}

}

render() {

return (

<div ref={this.listRef}>{/* ...contents... */}</div>

);

}

}

上述例子中重点是从 getSnapshotBeforeUpdate读取scrollHeight属性,因为“render”阶段生命周期(如 render)和“commit”阶段生命周期(如 getSnapshotBeforeUpdate 和 componentDidUpdate)之间可能存在延迟。

生命周期修改的深层原因

因为React 16引入了Fiber机制,把同步的渲染流程进化为了异步的渲染流程,这么做的原因是同步渲染流程有个弊端:一旦开始就不能停下,大工作量的渲染任务执行时,主线程会被长时间的占用,浏览器无法即时响应与用户的交互。

Fiber机制会把渲染任务拆解为多个小任务,并且每执行完一个小任务,就把主线程的执行权交出去,也就解决了上面的弊端。

然而,采用Fiber机制进行渲染时,render阶段没有副作用,可以被暂停,终止或重新启动。就是这个重新启动,会导致工作在render阶段的componentWillMount、componentWillReceiveProps、componentWillUpdate存在重复执行的可能,所以它们几个必须被替换掉。

三、Error boundaries

在React16之前,组件内的JS错误会导致React的内部状态被破坏,并且在下一次渲染时产生无法追踪的错误。这些错误基本上是由其他的非React组件代码错误引起的。但React并没有提供一种优雅的错误处理方式,也无法从错误中恢复。

然而部分UI的JS错误不应该导致整个应用的崩溃,为了解决这个问题,React引入了一个新的概念——错误边界。

Error boundaries(错误边界)是一个React组件,它可以捕获并打印发生在其子组件数任何位置的JS错误,然后,渲染出备用UI,而非崩溃了的子组件。错误边界可以在渲染期间、生命周期方法和整个组件树的构造函数中捕获错误。

但是错误边界无法捕获以下几种错误:

  • 事件处理
  • 异步代码
  • 服务端渲染
  • 自身错误

新版React给我们提供了两个与错误处理相关的API:static getDerivedStateFromError和componentDidCatch。

只要在Class组件中定义static getDerivedStateFromError或componentDidCatch这两个生命周期方法中的任意一个/两个,那么这个组件就是一个错误边界组件了。

static getDerivedStateFromError

getDerivedStateFromError是一个静态方法。在渲染子组件的过程中,页面更新之前,当发生了错误时,该方法就会运行。

它将抛出的错误作为参数并返回一个对象覆盖掉当前组件的state。

class ErrorBoundary extends React.Component {

constructor(props) {

super(props);

this.state = { hasError: false };

}

static getDerivedStateFromError(error) { // 更新 state 使下一次渲染可以显降级 UI return { hasError: true }; }

render() {

if (this.state.hasError) { // 你可以渲染任何自定义的降级 UI return <h1>Something went wrong.</h1>; }

return this.props.children;

}

}

componentDidCatch

componentDidCatch会在后代组件抛出错误时调用,因为它的执行时间比较晚,所以一般不支持改变组件state。

它接受两个参数:

error:抛出的错误;

info: 带有componentStack key的对象,其中包含有关组件引发错误的栈信息。

class ErrorBoundary extends React.Component {

constructor(props) {

super(props);

this.state = { hasError: false };

}

static getDerivedStateFromError(error) {

// 更新 state 使下一次渲染可以显示降级 UI

return { hasError: true };

}

componentDidCatch(error, info) { // "组件堆栈" 例子: // in ComponentThatThrows (created by App) // in ErrorBoundary (created by App) // in div (created by App) // in App logComponentStackToMyService(info.componentStack); }

render() {

if (this.state.hasError) {

// 你可以渲染任何自定义的降级 UI

return <h1>Something went wrong.</h1>;

}

return this.props.children;

}

}

四、总结

React 16基于两个原因做出了生命周期的调整:

 

  • 其一:为同步渲染改异步渲染的Fiber铺路,把有可能多次执行的render阶段中 componentWillMount/componentillUpdate/componentWillRecevieProps三个方法弃用;
  • 其二:为在一定程度上防止用户对生命周期的错用和滥用,把新增的getDerivedStateFromProps用static修饰,阻止用户在其内部使用this。


Tags:React   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
从0实现React18
要从零开始实现React 18,需要理解React的核心概念和一些主要特性。以下是一个简要的步骤:1. 了解React的基本概念: 组件: React应用的基本构建块。组件可以是函数组件(Functional...【详细内容】
2024-01-22  Search: React  点击:(47)  评论:(0)  加入收藏
React的核心概念
React是一个开源JavaScript库,用于构建用户界面。它由Facebook开发并维护,已成为构建Web和移动应用程序的流行选择。React的主要特点是组件化架构,它使开发人员能够将应用程序...【详细内容】
2024-01-09  Search: React  点击:(103)  评论:(0)  加入收藏
浅析五种 React 组件设计模式
作为一名 React 开发者,你可能会面临下面几个问题: 如何构建一个高复用度性的组件,使其适应不同的业务场景? 如何构建一个具有简单 API的组件,使其易于使用? 如何构建一个在 UI 和...【详细内容】
2024-01-09  Search: React  点击:(83)  评论:(0)  加入收藏
React与Vue性能对比:两大前端框架的性能
React和Vue是当今最流行的两个前端框架,它们在性能方面都有着出色的表现。React的加载速度:初次加载:由于React使用了虚拟DOM(Virtual DOM)技术,它可以通过比较虚拟DOM树与实际DOM...【详细内容】
2024-01-05  Search: React  点击:(107)  评论:(0)  加入收藏
Vanilla Design,新一代 React UI 库
这几天做需求,一堆 UI 库实在是不知道选哪个,各种角色的同事争论不休;还总有新轮子冒出来,所以我来插一脚,并借此来领悟写代码的哲学:The best way to write secure and reliable...【详细内容】
2024-01-04  Search: React  点击:(89)  评论:(0)  加入收藏
vue3中 ref和 reactive的区别 ?
最近有朋友在面试过程中经常被问到这么一个问题,vue3 中的ref 和 reactive的区别在哪里,为什么 要定义两个API 一个 api不能实现 响应式更新吗??带着这个疑问 ,我们 接下来进行逐...【详细内容】
2024-01-03  Search: React  点击:(38)  评论:(0)  加入收藏
React18 与 Vue3 全方面对比
1. 编程风格 & 视图风格1.1 编程风格 React 语法少、难度大;Vue 语法多,难度小例如指令:Vue<input v-model="username"/><ul> <li v-for="(item,index) in list" :key="inde...【详细内容】
2024-01-03  Search: React  点击:(72)  评论:(0)  加入收藏
使用React微前端的完整指南
译者 | 李睿审校 | 重楼事实表明,前端开发伴随着许多挑战。而寻找简化开发过程和加快任务执行的方法是每个开发团队的目标。在开发大型复杂产品时,让开发团队成员在任务上进行...【详细内容】
2023-12-26  Search: React  点击:(90)  评论:(0)  加入收藏
什么是React的错误边界(Error Boundary)?
React的错误边界(ErrorBoundary)是一种React组件,用于捕获并处理其子组件树中任何位置的JavaScript错误。它允许开发人员在应用程序中定义错误边界,以便在发生错误时显示备用UI...【详细内容】
2023-12-21  Search: React  点击:(125)  评论:(0)  加入收藏
如何设计更优雅的 React 组件?
在日常开发中,团队中每个人组织代码的方式不尽相同。下面我们就从代码结构的角度来看看如何组织一个更加优雅的 React 组件!1. 导入依赖项我们通常会在组件文件顶部导入组件所...【详细内容】
2023-12-21  Search: React  点击:(101)  评论:(0)  加入收藏
▌简易百科推荐
即将过时的 5 种软件开发技能!
作者 | Eran Yahav编译 | 言征出品 | 51CTO技术栈(微信号:blog51cto) 时至今日,AI编码工具已经进化到足够强大了吗?这未必好回答,但从2023 年 Stack Overflow 上的调查数据来看,44%...【详细内容】
2024-04-03    51CTO  Tags:软件开发   点击:(6)  评论:(0)  加入收藏
跳转链接代码怎么写?
在网页开发中,跳转链接是一项常见的功能。然而,对于非技术人员来说,编写跳转链接代码可能会显得有些困难。不用担心!我们可以借助外链平台来简化操作,即使没有编程经验,也能轻松实...【详细内容】
2024-03-27  蓝色天纪    Tags:跳转链接   点击:(13)  评论:(0)  加入收藏
中台亡了,问题到底出在哪里?
曾几何时,中台一度被当做“变革灵药”,嫁接在“前台作战单元”和“后台资源部门”之间,实现企业各业务线的“打通”和全域业务能力集成,提高开发和服务效率。但在中台如火如荼之...【详细内容】
2024-03-27  dbaplus社群    Tags:中台   点击:(9)  评论:(0)  加入收藏
员工写了个比删库更可怕的Bug!
想必大家都听说过删库跑路吧,我之前一直把它当一个段子来看。可万万没想到,就在昨天,我们公司的某位员工,竟然写了一个比删库更可怕的 Bug!给大家分享一下(不是公开处刑),希望朋友们...【详细内容】
2024-03-26  dbaplus社群    Tags:Bug   点击:(5)  评论:(0)  加入收藏
我们一起聊聊什么是正向代理和反向代理
从字面意思上看,代理就是代替处理的意思,一个对象有能力代替另一个对象处理某一件事。代理,这个词在我们的日常生活中也不陌生,比如在购物、旅游等场景中,我们经常会委托别人代替...【详细内容】
2024-03-26  萤火架构  微信公众号  Tags:正向代理   点击:(11)  评论:(0)  加入收藏
看一遍就理解:IO模型详解
前言大家好,我是程序员田螺。今天我们一起来学习IO模型。在本文开始前呢,先问问大家几个问题哈~什么是IO呢?什么是阻塞非阻塞IO?什么是同步异步IO?什么是IO多路复用?select/epoll...【详细内容】
2024-03-26  捡田螺的小男孩  微信公众号  Tags:IO模型   点击:(9)  评论:(0)  加入收藏
为什么都说 HashMap 是线程不安全的?
做Java开发的人,应该都用过 HashMap 这种集合。今天就和大家来聊聊,为什么 HashMap 是线程不安全的。1.HashMap 数据结构简单来说,HashMap 基于哈希表实现。它使用键的哈希码来...【详细内容】
2024-03-22  Java技术指北  微信公众号  Tags:HashMap   点击:(11)  评论:(0)  加入收藏
如何从头开始编写LoRA代码,这有一份教程
选自 lightning.ai作者:Sebastian Raschka机器之心编译编辑:陈萍作者表示:在各种有效的 LLM 微调方法中,LoRA 仍然是他的首选。LoRA(Low-Rank Adaptation)作为一种用于微调 LLM(大...【详细内容】
2024-03-21  机器之心Pro    Tags:LoRA   点击:(12)  评论:(0)  加入收藏
这样搭建日志中心,传统的ELK就扔了吧!
最近客户有个新需求,就是想查看网站的访问情况。由于网站没有做google的统计和百度的统计,所以访问情况,只能通过日志查看,通过脚本的形式给客户导出也不太实际,给客户写个简单的...【详细内容】
2024-03-20  dbaplus社群    Tags:日志   点击:(4)  评论:(0)  加入收藏
Kubernetes 究竟有没有 LTS?
从一个有趣的问题引出很多人都在关注的 Kubernetes LTS 的问题。有趣的问题2019 年,一个名为 apiserver LoopbackClient Server cert expired after 1 year[1] 的 issue 中提...【详细内容】
2024-03-15  云原生散修  微信公众号  Tags:Kubernetes   点击:(6)  评论:(0)  加入收藏
站内最新
站内热门
站内头条