react 16.3生命周期更新解析
React v16.3虽然是一个小版本升级,但是却对React组件生命周期函数有巨大变化。
文章梗概
- 新版本+2-3个生命周期。
- 为什么增加那2个生命周期?
- 为什么减去之前3个生命周期?
- 相关知识延伸
正文
React v16.0刚推出的时候,是增加了一个componentDidCatch生命周期函数(看文末错误边界),这只是一个增量式修改,完全不影响原有生命周期函数;但是,到了React v16.3,大改动来了,引入了两个新的生命周期函数:
- getDerivedStateFromProps
- getSnapshotBeforeUpdate
先来看React v16.3之前的生命周期函数(图中实际上少了componentDidCatch),如下图。


这个生命周期函数非常的对称,有componentWilUpdate对应componentDidUpdate,有componentWillMount对应componentDidMount;也考虑到了因为父组件引发渲染可能要根据props更新state的需要,所以有componentWillReceiveProps。
但是,这个生命周期函数的组合在Fiber(React Fiber是什么,看文末解释)之后就显得不合适了,因为,如果要开启async rendering,在render函数之前的所有函数,都有可能被执行多次。长期以来,原有的生命周期函数总是会诱惑开发者在render之前的生命周期函数做一些动作,现在这些动作还放在这些函数中的话,有可能会被调用多次,这肯定不是你想要的结果。
为什么不在componentWillMount里写AJAX获取数据的功能,他们的观点是,componentWillMount在render之前执行,早一点执行早得到结果。要知道,在componentWillMount里发起AJAX,不管多快得到结果也赶不上首次render,而且componentWillMount在服务器端渲染也会被调用到(当然,也许这是预期的结果),这样的IO操作放在componentDidMount里更合适。在Fiber启用async render之后,更没有理由在componentWillMount里做AJAX,因为componentWillMount可能会被调用多次,谁也不会希望无谓地多次调用AJAX吧。 道理说了都明白,但是历史经验告诉我们,不管多么地苦口婆心教导开发者不要做什么不要做什么,都不如直接让他们干脆没办法做。
随着getDerivedStateFromProps的推出,同时deprecate了一组生命周期API,包括:
- componentWillReceiveProps
- componentWillMount
- componentWillUpdate
可以看到,除了shouldComponentUpdate之外,render之前的所有生命周期函数全灭,就因为太多错用滥用这些生命周期函数的做法,预期追求对称的美学,不如来点实际的,让程序员断了在这些生命周期函数里做些不该做事情的念想。
至于shouldComponentUpdate,如果谁还想着在里面做AJAX操作,那真的是没救了。
按照官方说法,以前需要利用被deprecate的所有生命周期函数才能实现的功能,都可以通过getDerivedStateFromProps的帮助来实现。
这个getDerivedStateFromProps是一个静态函数,所以函数体内不能访问this,简单说,就是应该一个纯函数,纯函数是一个好东西啊,输出完全由输入决定。
static getDerivedStateFromProps(nextProps, prevState) {
//根据nextProps和prevState计算出预期的状态改变,返回结果会被送给setState
}
看到这样的函数声明,应该感受到React的潜台词:老实做一个运算就行,别在这里搞什么别的动作。
每当父组件引发当前组件的渲染过程时,getDerivedStateFromProps会被调用,这样我们有一个机会可以根据新的props和之前的state来调整新的state,如果放在三个被deprecate生命周期函数中实现比较纯,没有副作用的话,基本上搬到getDerivedStateFromProps里就行了;如果不幸做了类似AJAX之类的操作,首先要反省为什么自己当初这么做,然后搬到componentDidMount或者componentDidUpdate里面去。
所有被deprecate的生命周期函数,目前还凑合着用,但是只要用了,开发模式下会有红色警告,在下一个大版本(也就是React v17)更新时会彻底废弃。
React v16.3还引入了一个新的声明周期函数getSnapshotBeforeUpdate,这函数会在render之后执行,而执行之时DOM元素还没有被更新,给了一个机会去获取DOM信息,计算得到一个snapshot,这个snapshot会作为componentDidUpdate的第三个参数传入。
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log('#enter getSnapshotBeforeUpdate');
return 'foo';
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log('#enter componentDidUpdate snapshot = ', snapshot);
}
上面这段代码可以看出来这个snapshot怎么个用法,snapshot咋看还以为是组件级别的某个“快照”,其实可以是任何值,到底怎么用完全看开发者自己,getSnapshotBeforeUpdate把snapshot返回,然后DOM改变,然后snapshot传递给componentDidUpdate。
官方给了一个例子,用getSnapshotBeforeUpdate来处理scroll,坦白说,我也想不出其他更常用更好懂的需要getSnapshotBeforeUpdate的例子,这个函数应该大部分开发者都用不上(听得懂我的潜台词吗:不要用!)
所以,React v16.3之后的生命周期函数一览图成了这样。


可以注意到,说getDerivedStateFromProps取代componentWillReceiveProps是不准确的,因为componentWillReceiveProps只在Updating过程中才被调用,而且只在因为父组件引发的Updating过程中才被调用);而getDerivedStateFromProps在Updating和Mounting过程中都会被调用。
此外,从上面这个也看得出来,同样是Updating过程,如果是因为自身setState引发或者forceUpdate引发,而不是不由父组件引发,那么getDerivedStateFromProps也不会被调用。
这其实容易引发一些问题,不用仔细想,光是由此让开发者不得不理解这乱七八糟的差异,就可以知道这是一个大坑! 还好,React很快意识到这个问题,在React v16.4中改正了这一点,改正的结果,就是让getDerivedStateFromProps无论是Mounting还是Updating,也无论是因为什么引起的Updating,全部都会被调用。
这样简单多了!
总结
用一个静态函数getDerivedStateFromProps来取代被deprecate的几个生命周期函数,就是强制开发者在render之前只做无副作用的操作,而且能做的操作局限在根据props和state决定新的state,而已。
这是进一步施加约束,防止开发者乱来,我说过,施加约束的哲学指导思想,是我最爱React的原因。
延伸
生命周期定义错误边界
https://zh-hans.reactjs.org/docs/error-boundaries.html 如果一个 class 组件中定义了 static getDerivedStateFromError() 或 componentDidCatch() 这两个生命周期方法中的任意一个(或两个)时,那么它就变成一个错误边界。当抛出错误后,请使用 static getDerivedStateFromError() 渲染备用 UI ,使用 componentDidCatch() 打印错误信息。
React Fiber是个什么东西呢?
官方的一句话解释是“React Fiber是对核心算法的一次重新实现”。这么说似乎太虚无缥缈,所以还是要详细说一下。
不用太紧张,不要以为React Fiber的到来是一场大革命,实际上,对我们只是把React当做工具的开发者来说,很可能感觉不到有什么功能变化。等到React v16发布的时候,我们修改package.json中的react版本号,重新npm install,一切就搞定了,然后我们就感觉到网页性能更高了一些,如此而已。
之前react更新一个组件,主线程专心运行,浏览器主线程被React占着呢,抽不出空,最后的结果就是用户敲了按键看不到反应,等React更新过程结束之后,咔咔咔那些按键一下子出现在input元素里了。很不好的体验。
更新以后,破解JavaScript中同步操作时间过长的方法其实很简单——分片。它把一个耗时长的任务分成很多小片,每一个小片的运行时间很短,虽然总时间依然很长,但是在每个小片执行完之后,都给其他任务一个执行的机会,这样唯一的线程就不会被独占,其他任务依然有运行的机会。
维护每一个分片的数据结构,就是Fiber。
react官网生命周期说明: https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
react 16.3生命周期更新解析的更多相关文章
- React 16.4 生命周期
补一下 React 16.4版本的生命周期图
- react学习小结(生命周期- 实例化时期 - 存在期- 销毁时期)
react学习小结 本文是我学习react的阶段性小结,如果看官你是react资深玩家,那么还请就此打住移步他处,如果你想给一些建议和指导,那么还请轻拍~ 目前团队内对react的使用非常普遍,之 ...
- React组件和生命周期简介
React 简介----React 是 Facebook 出品的一套颠覆式的前端开发类库.为什么说它是颠覆式的呢? 内存维护虚拟 DOM 对于传统的 DOM 维护,我们的步骤可能是:1.初始化 ...
- 2. React组件的生命周期
2. React组件的生命周期 使用React开发时候用到最多的就是React的组件了,通过继承React.Component,加入constructor构造函数,实现Render方法即可.这当中Re ...
- Vue与React的异同 -生命周期
vue的生命周期 创建前 beforeCreate 创建 create 挂载前 beforeMount 挂载 mounted 更新前 beforeUpdate 更新 updated 销毁前 bef ...
- React 组件的生命周期方法
React 组件的生命周期方法 按渲染顺序: 1: componentWillMount() – 在渲染之前执行,在客户端和服务器端都会执行. 2: componentDidMount() – 仅在第 ...
- React 之 组件生命周期
React 之 组件生命周期 理解1) 组件对象从创建到死亡它会经历特定的生命周期阶段2) React组件对象包含一系列的勾子函数(生命周期回调函数), 在生命周期特定时刻回调3) 我们在定义组件时, ...
- 16、生命周期-BeanPostProcessor原理
16.生命周期-BeanPostProcessor原理 16.1 打断点运行postProcessBeforeInitialization 可以看到先执行的顺序为: applyBeanPostProc ...
- react第三单元(react组件的生命周期)
第三单元(react组件的生命周期) #课程目标 灵活掌握react组件的生命周期以及组件的活动过程. 能够灵活使用react的生命周期 #知识点 react的类组件的生命周期分为三个阶段 实例期 存 ...
- react js 之生命周期
react redux 结合是目前比较流行的前端开发框架,主要基于react 中的state 树为数据模型,借助redux 来控制 state 数据:下面直接从代码层面解析该框架中一个react 组件 ...
随机推荐
- [最长回文字符串]manacher马拉车
manacher马拉车 https://www.luogu.com.cn/problem/P3805 闲言一下:花了一个中午终于把 manacher 给搞懂了.本文将以一个蒟蒻的身份来,来写写马拉车算 ...
- Java理论(一)
什么是java Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承.指针等概念,因 此Java语言具有功能强大和简单易用两个特征.Java语言作为静态面向 ...
- MAUI Blazor 显示本地图片的新思路
前言 好久没写文章了,水一篇 关于MAUI Blazor 显示本地图片这个问题,有大佬发过了. 就是 token 大佬的那篇 Blazor Hybrid (Blazor混合开发)更好的读取本地图片 主 ...
- PostgreSQL 9.6 文档: 数据类型
章 8. 数据类型 目录 8.1. 数字类型 8.1.1. 整数类型 8.1.2. 任意精度数字 8.1.3. 浮点类型 8.1.4. 序数类型 8.2. 货币类型 8.3. 字符类型 8.4. 二进 ...
- 用React仿钉钉审批流
引言 这几天帮朋友忙,用了一周时间,高仿了一个钉钉审批流.这个东西会有不少朋友有类似需求,就分享出来,希望能有所帮助.为了方便朋友的使用,设计制作的时候,尽量做到节点配置可定制,减少集成成本.如果您的 ...
- 领域驱动设计(DDD):从基础代码探讨高内聚低耦合的演进
大家好,我是付威,一名已在编码第一线奋斗了十余年的程序员.在2019年我初次接触到领域驱动设计(Domain-Driven Design,简称DDD)的概念.在我的探索中,我发现许多有关DDD的教程过 ...
- Doris 再次启动FE失败的思考
Doris再次启动FE失败的思考 背景描述 在昨天已经成功下载安装最新稳定版docker.拉取doris-0.15.0版本的镜像.将镜像挂载道本地Doris源码目录.完成了doris的编译之后,今天在 ...
- 《Kali渗透基础》11. 无线渗透(一)
@ 目录 1:无线技术 2:IEEE 802.11 标准 2.1:无线网络分层 2.2:IEEE 2.3:日常使用标准 2.3.1:802.11 2.3.2:802.11b 2.3.3:802.11a ...
- Vue【原创】可拖动列表 darg-list
可拖动排序的列表 drag-list,这个比较简单易懂,拿例子直接运行看效果就好了. 组件代码: 1 <template> 2 <ul class="list" ...
- 现代 CSS 解决方案:数学函数 Round
在 CSS 中,存在许多数学函数,这些函数能够通过简单的计算操作来生成某些属性值,例如在现代 CSS 解决方案:CSS 数学函数一文中,我们详细介绍了 calc():用于计算任意长度.百分比或数值型数 ...