react v16.x更新了组件生命周期,将
componentWillReceiveProps
、
componentWillMount
、
componentWillUpdate
这三个钩子函数丢弃了,然后增加了
getDerivedStateFromProps
、
getSnapshotBeforeUpdate
。
废弃原因
因为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 生命周期,不会再触发别的生命周期