React v16.x 之深入理解组件生命周期


react v16.x更新了组件生命周期,将 componentWillReceivePropscomponentWillMountcomponentWillUpdate这三个钩子函数丢弃了,然后增加了 getDerivedStateFromPropsgetSnapshotBeforeUpdate

废弃原因

因为React v16.x 版本更新了diff机制,引入了React Fiber(不了解Fiber的可查看)。Fiber机制是异步操作,可能在执行中暂停然后再继续执行,这么就有可能导致在render函数之前的所有函数都有可能被执行多次。

componentWillMount

为什么不在componentWillMount中发起ajax?
答:因为在 componentWillMount中发起ajax,不管多块结果也赶不上首次render,ajax请求数据在组件挂载之前回来导致setState无效。因为setState 是将更新的状态放进了组件的__pendingStateQueue队列中。react并不会立即响应更新,会等到组件挂载完成后,再统一更新脏组件。
React16之后采用了Fiber架构,只有componentDidMount声明周期函数是确定被执行一次的,ComponentWillMount的生命周期钩子都有可能执行多次。

componentWillReceiveProps

在初始props不会被调用,它会在组件接受到新的props时调用。一般用于父组件更新状态时子组件的重新渲染。在react16.3之前,componentWillReceiveProps是在不进行额外render的前提下,响应props中的改变并更新state的唯一方式。即使当前组件不更新,只要父组件更新也会引发这个函数被调用。可以setState,可能导致增加组件重绘制,甚至无限重绘(判断失效,不断setState)

componentWillUpdate

可以通过props改变触发回调,但是Fiber以后出了componentDidUpdate
componentDidMount只会调用一次,componentWillUpdate会被调用多次,这样回调函数也会调用多次不合理。

新的生命周期

在这里插入图片描述

定义

constructor()

作用:

  • 初始化函数内部 state
  • 绑定实例,eg:this.handleClick = this.handleClick.bind(this);

1、constructor() 在React组件挂载之前被调用,在为React.Component子类实现构造函数时,应在其他语句之前调用 super()
2、如果不初始化 state 或不进行方法绑定,则不需要写 constructor() , 只需要设置 this.state 即可
3、不能在 constructor()构造函数内部调用 this.setState(), 因为此时第一次 render()还未执行,也就意味DOM节点还未挂载

static getDerivedStateFromProps(nextProps, prevState)

getDerivedStateFromProps() 在调用 render方法之前调用,在初始化和后续更新都会被调用。
每当父组件引发当前组件的渲染过程时,getDerivedStateFromProps会被调用,可以根据新的props和之前的state来调整新的state
⚠️注意: getDerivedStateFromProps是一个静态函数,函数体内不能访问this

// nextProps:即将更新的 props
// prevState:上一个状态的 state 
// return:返回一个对象来更新 state, 如果返回 null 则不更新任何内容
// 作用:可以比较props 和 state来加一些限制条件,防止无用的state更新
static getDerivedStateFromProps(nextProps, prevState) {
  //根据nextProps和prevState计算出预期的状态改变,返回结果会被送给setState
}
shouldComponentUpdate(nextProps, nextState)

shouldComponentUpdate() 在组件更新之前调用,可以控制组件是否进行更新, 返回true时组件更新, 返回false则不更新。

// nextProps:即将更新的 props
// nextState:即将跟新后的 state 值
// 作用:可以根据更新前后的 props 或 state 来比较加一些限制条件,决定是否更新,进行性能优化
// 注意:不要 shouldComponentUpdate 中调用 setState(),否则会导致无限循环调用更新、渲染,直至浏览器内存崩溃
shouldComponentUpdate(nextProps, nextState) {
 	...
}
render()

用于渲染DOM, render()方法必须返回reactDOM
注意:不要在 render 里面 setState, 否则会触发死循环导致内存崩溃

getSnapshotBeforeUpdate(prevProps, prevState)

getSnapshotBeforeUpdate() 在最近一次的渲染输出被提交之前调用。也就是说,在 render 之后,即将对组件进行挂载时调用。

它可以使组件在 DOM 真正更新之前捕获一些信息(例如滚动位置),此生命周期返回的任何值都会作为参数传递给 componentDidUpdate()。如不需要传递任何值,那么请返回 null

componentDidMount()

componentDidMount() 在组件挂载后 (插入DOM树后) 立即调用,componentDidMount() 是发送网络请求、启用事件监听方法的好时机,并且可以在 此钩子函数里直接调用 setState()

componentDidUpdate(prevProps, prevState, snapshot)

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

// prevProps:上一次props值
// prevState:上一次state值
// snapshot: 如果组件实现了 getSnapshotBeforeUpdate(),第三个是“snapshot” 参数传递
// 作用:可以进行前后props的比较进行条件语句的限制,来进行 setState() , 否则会导致死循环
componentDidUpdate(prevProps, prevState, snapshot) {
 	...
}
componentWillUnmount()

componentWillUnmount() 在组件即将被卸载或销毁时进行调用。此生命周期是取消网络请求、移除监听事件、清理 DOM 元素、清理定时器等操作的好时机

执行顺序

创建时
constructor()
static getDerivedStateFromProps()
render()
componentDidMount()

更新时
static getDerivedStateFromProps()
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate()
componentDidUpdate()

卸载时
componentWillUnmount()

demo

代码地址

第一次渲染

控制台的打印顺序为:

Parent 组件: constructor()
Parent 组件: getDerivedStateFromProps()
Parent 组件: render()
Child 组件: constructor()
Child 组件: getDerivedStateFromProps()
Child 组件: render()
Child 组件: componentDidMount()
Parent 组件:componentDidMount()

子组件修改自身状态 state

控制台的打印顺序为:

Child 组件: getDerivedStateFromProps()
Child 组件: shouldComponentUpdate()
Child 组件: render()
Child 组件: getSnapshotBeforeUpdate()
Child 组件: componentDidUpdate()

修改父组件中传入子组件的 props

控制台的打印顺序为:

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 以及 render之后的声明周期,则子组件先执行,并且是与父组件交替执行

当子组件进行卸载时,只会执行自身的 componentWillUnmount 生命周期,不会再触发别的生命周期

参考链接
React v16.3之后的组件生命周期函数
深入详解React生命周期

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值