在说新的生命周期之前,我们先了解下原来的生命周期:

  1. 挂载卸载过程
    1.1.constructor()
    constructor()中完成了React数据的初始化,它接受两个参数:props和context,当想在函数内部使用这两个参数时,需使用super()传入这两个参数。
    注意:只要使用了constructor()就必须写super(),否则会导致this指向错误。

1.2.componentWillMount()
componentWillMount()一般用的比较少,它更多的是在服务端渲染时使用。它代表的过程是组件已经经历了constructor()初始化数据后,但是还未渲染DOM时。

1.3.componentDidMount()
组件第一次渲染完成,此时dom节点已经生成,可以在这里调用ajax请求,返回数据setState后组件会重新渲染

1.4.componentWillUnmount ()
在此处完成组件的卸载和数据的销毁。

  • clear你在组建中所有的setTimeout,setInterval
  • 移除所有组建中的监听 removeEventListener
  • 有时候我们会碰到这个warning:
Can only update a mounted or mounting component. This usually
means you called setState() on an unmounted component. This is a   no-op. Please check the code for the undefined component.

原因:因为你在组件中的ajax请求返回setState,而你组件销毁的时候,请求还未完成,因此会报warning
解决方法:

componentDidMount() {this.isMount === trueaxios.post().then((res) => {this.isMount && this.setState({   // 增加条件ismount为true时aaa:res})
})
}
componentWillUnmount() {this.isMount === false
}
  1. 更新过程
    2.1. componentWillReceiveProps (nextProps)
  • 在接受父组件改变后的props需要重新渲染组件时用到的比较多
  • 接受一个参数nextProps
  • 通过对比nextProps和this.props,将nextProps的state为当前组件的state,从而重新渲染组件
componentWillReceiveProps (nextProps) {nextProps.openNotice !== this.props.openNotice&&this.setState({openNotice:nextProps.openNotice},() => {console.log(this.state.openNotice:nextProps)//将state更新为nextProps,在setState的第二个参数(回调)可以打         印出新的state})
}

2.2.shouldComponentUpdate(nextProps,nextState)

  • 主要用于性能优化(部分更新)
  • 唯一用于控制组件重新渲染的生命周期,由于在react中,setState以后,state发生变化,组件会进入重新渲染的流程,在这里return false可以阻止组件的更新
  • 因为react父组件的重新渲染会导致其所有子组件的重新渲染,这个时候其实我们是不需要所有子组件都跟着重新渲染的,因此需要在子组件的该生命周期中做判断

2.3.componentWillUpdate (nextProps,nextState)
shouldComponentUpdate返回true以后,组件进入重新渲染的流程,进入componentWillUpdate,这里同样可以拿到nextProps和nextState。

2.4.componentDidUpdate(prevProps,prevState)
组件更新完毕后,react只会在第一次初始化成功会进入componentDidmount,之后每次重新渲染后都会进入这个生命周期,这里可以拿到prevProps和prevState,即更新前的props和state。

2.5.render()
render函数会插入jsx生成的dom结构,react会生成一份虚拟dom树,在每一次组件更新时,在此react会通过其diff算法比较更新前后的新旧DOM树,比较以后,找到最小的有差异的DOM节点,并重新渲染。

接下来我们看看更新后的生命周期:

先看看它的变化:

新增:getDerivedStateFromProps,getSnapshotBeforeUpdate
UNSAFE:UNSAFE_componentWillMount,UNSAFE_componentWillUpdate,UNSAFE_componentWillReceivePropsd

React 官方正式发布了 v16.3 版本。在这次的更新中,除了前段时间被热烈讨论的新 Context API 之外,新引入的两个生命周期函数 getDerivedStateFromProps,getSnapshotBeforeUpdate 以及在未来 v17.0 版本中即将被移除的三个生命周期函数 componentWillMount,componentWillReceiveProps,componentWillUpdate .

getDerivedStateFromProps

React生命周期的命名一直都是非常语义化的,这个生命周期的意思就是从props中获取state,可以说是太简单易懂了。可以说,这个生命周期的功能实际上就是将传入的props映射到state上面。由于16.4的修改,这个函数会在每次re-rendering之前被调用,这意味着什么呢?意味着即使你的props没有任何变化,而是父state发生了变化,导致子组件发生了re-render,这个生命周期函数依然会被调用。看似一个非常小的修改,却可能会导致很多隐含的问题。

使用

这个生命周期函数是为了替代componentWillReceiveProps存在的,所以在你需要使用componentWillReceiveProps的时候,就可以考虑使用getDerivedStateFromProps来进行替代了。

两者的参数是不相同的,而getDerivedStateFromProps是一个静态函数,也就是这个函数不能通过this访问到class的属性,也并不推荐直接访问属性。而是应该通过参数提供的nextProps以及prevState来进行判断,根据新传入的props来映射到state。

需要注意的是,如果props传入的内容不需要影响到你的state,那么就需要返回一个null,这个返回值是必须的,所以尽量将其写到函数的末尾。

// 在getDerivedStateFromProps中进行state的改变static getDerivedStateFromProps({ error, value }, state) {/* Keep last received error in state */if (error && error !== state.error) {return { error };}if (value && value !== state.text) {return { text: value }}return null;}

Case1 -- 多来源的不同状态

假设我们有一个列表,这个列表受到页面主体,也就是根组件的驱动,也受到其本身数据加载的驱动。

因为这个页面在开始渲染的时候,所有的数据请求可能是通过batch进行的,所以要在根组件进行统一处理,而其列表的分页操作,则是由其本身控制。

这会出现什么问题呢?该列表的状态受到两方面的控制,也就是re-render可能由props驱动,也可能由state驱动。这就导致了getDerivedStateFromProps会在两种驱动状态下被重新渲染。

当这个函数被多次调用的时候,就需要注意到,state和props的变化将会怎样影响到你的组件变化。

// 组件接收一个type参数
static propTypes = {type: PropTypes.number
}// 组件还具有自己的状态来渲染列表
class List extends React.Component {constructor(props) {super(props);this.state = {list: [],type: 0,}}
}

如上面代码的例子所示,组件既受控,又控制自己。当type发生变化,会触发一次getDerivedStateFromProps,在这里更新组件的type状态,然而,在进行异步操作之后,组件又会更新list状态,这时你的getDerivedStateFromProps函数就需要注意,不能够仅仅判断type是否变化来更新状态,因为list的变化也会更新到组件的状态。这时就必须返回一个null,否则会导致组件无法更新并且报错。

Case2 -- 组织好你的组件

考虑一下,如果你的组件内部既需要修改自己的type,又需要接收从外部修改的type。

是不是非常混乱?getDerivedStateFromProps中你根本不知道该做什么。

static getDerivedStateFromProps(nextProps, prevState) {const {type} = nextProps;// type可能由props驱动,也可能由state驱动,这样判断会导致state驱动的type被回滚if (type !== prevState.type) {return {type,};}// 否则,对于state不进行任何操作return null;
}

如何解决这个棘手的问题呢?

好好组织你的组件,在非必须的时候,摒弃这种写法。type要么由props驱动,要么完全由state驱动。
如果实在没有办法解耦,那么就需要一个hack来辅助:绑定props到state上。

constructor(props) {super(props);this.state = {type: 0,props,}
}
static getDerivedStateFromProps(nextProps, prevState) {const {type, props} = nextProps;// 这段代码可能看起来非常混乱,这个props可以被当做缓存,仅用作判断if (type !== props.type) {return {type,props: {type,},};}// 否则,对于state不进行任何操作return null;
}

上面的代码可以保证在进行多数据源驱动的时候,状态能够正确改变。当然,这样的代码很多情况下是会影响到别人阅读你的代码的,对于维护造成了非常大的困难。

从这个生命周期的更新来看,react更希望将受控的propsstate进行分离,就如同Redux作者Dan Abramov在redux文档当中写的一样Presentational and Container Components,将所有的组件分离称为展示型组件和容器型组件,一个只负责接收props来改变自己的样式,一个负责保持其整个模块的state。这样可以让代码更加清晰。但是在实际的业务逻辑中,我们有时很难做到这一点,而且这样可能会导致容器型组件变得非常庞大以致难以管理,如何进行取舍还是需要根据实际场景决定的。

Case3 -- 异步

以前,我们可以在props发生改变的时候,在componentWillReceiveProps中进行异步操作,将props的改变驱动到state的改变。

componentWillReceiveProps(nextProps) {if (props.type !== nextProps.type) {// 在这里进行异步操作或者更新状态this.setState({type: props.type,});this._doAsyncOperation();}
}

这样的写法已经使用了很久,并且并不会存在什么功能上的问题,但是将componentWillReceiveProps标记为deprecated的原因也并不是因为功能问题,而是性能问题。

当外部多个属性在很短的时间间隔之内多次变化,就会导致componentWillReceiveProps被多次调用。这个调用并不会被合并,如果这次内容都会触发异步请求,那么可能会导致多个异步请求阻塞。

getDerivedStateFromProps is invoked right before calling the render method, both on the initial mount and on subsequent updates. It should return an object to update the state, or null to update nothing.

这个生命周期函数会在每次调用render之前被触发,而读过一点react源码的童鞋都会了解,reactsetState操作是会通过transaction进行合并的,由此导致的更新过程是batch的,而react中大部分的更新过程的触发源都是setState,所以render触发的频率并不会非常频繁(感谢 @leeenx20 的提醒,这里描述进行了修改)。

在使用getDerivedStateFromProps的时候,遇到了上面说的props在很短的时间内多次变化,也只会触发一次render,也就是只触发一次getDerivedStateFromProps。这样的优点不言而喻。

那么如何使用getDerivedStateFromProps进行异步的处理呢?

If you need to perform a side effect (for example, data fetching or an animation) in response to a change in props, use componentDidUpdate lifecycle instead.

官方教你怎么写代码系列,但是其实也没有其他可以进行异步操作的地方了。为了响应props的变化,就需要在componentDidUpdate中根据新的props和state来进行异步操作,比如从服务端拉取数据。

// 在getDerivedStateFromProps中进行state的改变
static getDerivedStateFromProps(nextProps, prevState) {if (nextProps.type !== prevState.type) {return {type: nextProps.type,};}return null;
}
// 在componentDidUpdate中进行异步操作,驱动数据的变化
componentDidUpdate() {this._loadAsyncData({...this.state});
}

这两者的区别在于:

在 React 开启异步渲染模式后,在 render 阶段读取到的 DOM 元素状态并不总是和 commit 阶段相同,这就导致在
componentDidUpdate 中使用 componentWillUpdate 中读取到的 DOM 元素状态是不安全的,因为这时的值很有可能已经失效了。
getSnapshotBeforeUpdate 会在最终的 render 之前被调用,也就是说在 getSnapshotBeforeUpdate 中读取到的 DOM 元素状态是可以保证与 componentDidUpdate 中一致的。
此生命周期返回的任何值都将作为参数传递给componentDidUpdate()。

小结

react为了防止部分开发者滥用生命周期,可谓非常尽心尽力了。既然你用不好,我就干脆不让你用。一个静态的生命周期函数可以让状态的修改更加规范和合理。

React新生命周期--getDerivedStateFromProps、getSnapshotBeforeUpdate相关推荐

  1. react新生命周期

    #react生命周期 组件生命周期的三个阶段: mounting updating unmounting ##旧的生命周期 #Mounting(加载阶段:涉及6个钩子函数) ##construtor( ...

  2. react之生命周期

    目录 一.什么是react? 二.react的生命周期 三.对于react生命周期的思考 熟练使用react开发项目之后,最近在学习react的底层原理以及源码,顺便总结一下react的生命周期. 一 ...

  3. React的生命周期(面试必问)

    React的生命周期 两张图带你理解 React的生命周期 React的生命周期(旧) class Life extends React.Component{// 构造器constructor(pro ...

  4. 附实例!图解React的生命周期及执行顺序

    本文由云+社区发表 作者:前端林子 1.七个可选的生命周期 可以结合下图来看: (1) componentWillMount() 仅在render()方法前被调用一次,如果在该方法中调用了setSta ...

  5. react初识生命周期

    好多朋友都在问我react生命周期执行的顺序 今天就说一下吧, react有三种构建组件的方法: 第一呢,就是React.createClass(),ES5写法现在用的比较少 第二呢:ES6 clas ...

  6. react组件生命周期_React组件生命周期-挂钩/方法介绍

    react组件生命周期 React components have several lifecycle methods that you can override to run your code a ...

  7. [react] 说说react的生命周期有哪些?

    [react] 说说react的生命周期有哪些? 装载阶段 组件第一次被渲染时的阶段,这一阶段相关的生命周期函数有: constructor componentWillMount render com ...

  8. react常用生命周期流程图

    简述 本文将常用的react生命周期绘制成了一张流程图,便于理解和记忆; react中每个组件都包含"生命周期方法",我们可以重写这些方法,以便于在运行过程中特定的阶段执行这些方法 ...

  9. 简单认识React的生命周期

    一. React的生命周期 这里render渲染函数会执行两次,第一次是当组件初始化完成的时候,第二次是当组件完成从数据的修改的时候再执行一次 1.创建和初始化到的生命周期: (1) getDefau ...

最新文章

  1. 告别2019,展望2020:让我们看一看这十年中深度学习的经典瞬间
  2. 《网站分析师实战指南》一1.4 进入实战之路
  3. php sqlserver扩展,PHP---连接sqlserver扩展配置
  4. CentOS 卸载OpenJdk和Tomcat开机启动
  5. efcore根据多个条件更新_EFCore.Sharding(EFCore开源分表框架)
  6. strlwr,strupr函数
  7. 缓存通用管理类 + 缓存 HttpContext.Current.Cache 和 HttpRuntime.Cache 的区别
  8. 不插电的计算机科学百度云,【精品】不插电的计算机科学.pdf
  9. vb从入门到精通_干货|让你 ArcGIS Engine从入门到精通的22个视频
  10. 怎么把证件照压缩在30K之内?教你三秒快速压缩证件照
  11. 纯H5+CSS3实现下雨特效
  12. 大数据Hive学习案例(2)——基于汽车销售的日志数据分析
  13. 变频电源使用脉冲宽度调制方式会对周边设备造成什么影响,该如何去防护
  14. ✨ StarRocks 6 月社区动态
  15. 带你了解中国互联网发展报告
  16. 公众号榜单 | 2020·8月公众号原创排行榜重磅发布
  17. 影响线型缩聚物分子量的因素_线型缩聚物的分子量与分子量分布.ppt
  18. Windows Update自动更新
  19. 关于移动硬盘或U盘数据丢失恢复工具
  20. WiFi 移植记录及心得 一

热门文章

  1. 基于FPGA的自动小车控制设计Verilog开发
  2. DES 加密算法的实现
  3. 如何搭建一个公链?公链生态“吸金“七件套,缺一不可
  4. 在D-H参数法里为什么只用四个参数就能完全定义一个具有6自由度的坐标系
  5. 蘑菇街服务器信息,蘑菇街TeamTalk服务器端MsgServer分析(一)
  6. leetcode|经典力扣第一题
  7. 【Unity VFX】VFX特效入门笔记-火花制作
  8. 百度,你大爷回来了!谷歌宣布AI重返中国,首席扛把子竟是个华人小姐姐
  9. MongoDB查询之exists
  10. 反射——反射的优缺点和优化