React 的生命周期 API 一直以来十分稳定,但是当 React 团队在引入异步渲染机制的时候,发现之前的生命周期会的使用产生一些问题,所以才会改动生命周期 API。
在 React 16.3 中,为下面三个生命周期钩子加上了 UNSAFE 标记:
- UNSAFE_componentWillMount
- UNSAFE_componentWillReceiveProps
- UNSAFE_componentWillUpdate
新增了下面两个生命周期方法:
- static getDerivedStateFromProps
- getSnapshotBeforeUpdate
在目前16.X(X>3)的 React 中,使用 componentWillMount, componentWillReceiveProps, and componentWillUpdate 这三个方法会收到警告。React 团队计划在 17.0 中测地废弃掉这几个 API。
新的生命周期钩子static getDerivedStateFromProps
class Example extends React.Component {
static getDerivedStateFromProps(props, state) {
// ...
}
}
React 在实例化组件之后以及重新渲染组件之前,将调用新的静态 getDerivedStateFromProps 生命周期方法。该方法类似于 componentWillReceiveProps,可以用来控制 props 更新 state 的过程。它返回一个对象表示新的 state。如果不需要更新组件,返回 null 即可。
getDerivedStateFromProps 与 componentDidUpdate一起将会替换掉所有的 componentWillReceiveProps。
新的生命周期钩子: getSnapshotBeforeUpdate
class Example extends React.Component {
getSnapshotBeforeUpdate(prevProps, prevState) {
// ...
}
}
getSnapshotBeforeUpdate 方法在 React 对视图做出实际改动(如 DOM 更新)发生前被调用,返回值将作为 componentDidUpdate 的第三个参数。
getSnapshotBeforeUpdate 配合 componentDidUpdate 可以取代 componentWillUpdate。
为何移除 componentWillMount
因为在 React 未来的版本中,异步渲染机制可能会导致单个组件实例可以多次调用该方法。很多开发者目前会将事件绑定、异步请求等写在 componentWillMount 中,一旦异步渲染时 componentWillMount 被多次调用,将会导致:
- 进行重复的时间监听,无法正常取消重复的 Listener,更有可能导致内存泄漏
- 发出重复的异步网络请求,导致 IO 资源被浪费
- 在服务端渲染时,componentWillMount 会被调用,但是会因忽略异步获取的数据而浪费 IO 资源
为何移除 componentWillUpdate
大多数开发者使用 componentWillUpdate 的场景是配合 componentDidUpdate,分别获取 rerender 前后的视图状态,进行必要的处理。但随着 React 新的 suspense、time slicing、异步渲染等机制的到来,render 过程可以被分割成多次完成,还可以被暂停甚至回溯,这导致 componentWillUpdate 和 componentDidUpdate 执行前后可能会间隔很长时间,足够使用户进行交互操作更改当前组件的状态,这样可能会导致难以追踪的 BUG。
React 新增的 getSnapshotBeforeUpdate 方法就是为了解决上述问题,因为 getSnapshotBeforeUpdate 方法是在 componentWillUpdate 后(如果存在的话),在 React 真正更改 DOM 前调用的,它获取到组件状态信息更加可靠。
除此之外,getSnapshotBeforeUpdate 还有一个十分明显的好处:它调用的结果会作为第三个参数传入 componentDidUpdate,避免了 componentWillUpdate 和 componentDidUpdate 配合使用时将组件临时的状态数据存在组件实例上浪费内存,getSnapshotBeforeUpdate 返回的数据在 componentDidUpdate 中用完即被销毁,效率更高。