React v16.4 的生命周期
依然是先看图:
变更缘由
原来( React v16.0 前)的生命周期在 React v16.0 推出的 Fiber 之后就不合适了,因为如果要开启 async rendering, 在 render 函数之前的所有函数,都有可能被执行多次。
有上一篇我们知道下面的这些生命周期都是原来( React v16.0 前)在 render 前执行的:
- componentWillMount
- componentWillReceiveProps
- shouldComponentUpdate
- componentWillUpdate
除了 shouldComponentUpdate ,其他在 render 函数之前的 所有函数( componentWillMount,componentWillReceiveProps,componentWillUpdate)都被 getDerivedStateFromProps 替代。
也就是用一个静态函数 getDerivedStateFromProps 来取代被 deprecate 的几个生命周期函数,就是强制开发者在 render 之前只做无副作用的操作,而且能做的操作局限在根据 props 和 state 决定新的 state 。
React v16.0 刚推出的时候,是增加了一个 componentDidCatch 生命周期函数,这只是一个增量式修改,完全不影响原有生命周期函数;但是,到了 React v16.3 ,大改动来了,引入了两个新的生命周期函数。
新引入了两个新的生命周期函数: getDerivedStateFromProps,getSnapshotBeforeUpdate
- getDerivedStateFromProps
getDerivedStateFromProps 本来( React v16.3 中)是只在创建和更新(由父组件引发部分),也就是不是不由父组件引发,那么 getDerivedStateFromProps 也不会被调用,如自身 setState 引发或者 forceUpdate 引发。
这样的话理解起来有点乱,在 React v16.4 中改正了这一点,让 getDerivedStateFromProps 无论是 Mounting 还是 Updating,也无论是因为什么引起的 Updating ,全部都会被调用,具体可看 React v16.4 的生命周期图。
在 React v16.4 后, getDerivedStateFromProps(props, state) 在组件创建时和更新时的 render 方法之前调用,它应该返回一个对象来更新状态,或者返回 null 来不更新任何内容。
- getSnapshotBeforeUpdate
getSnapshotBeforeUpdate() 被调用于 render 之后,可以读取但无法使用 DOM 的时候。它使您的组件可以在可 能更改之前从 DOM 捕获一些信息(例如滚动位置)。此生命周期返回的任何值都将作为参数传递给 componentDidUpdate() 。
官网给的例子:
class ScrollingList extends React.Component {
constructor(props) {
super(props);
this.listRef = React.createRef();
}
getSnapshotBeforeUpdate(prevProps, prevState) { //我们是否要添加新的 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>
);
}