谈谈 React 新的生命周期钩子

在 React 16.3 中,Facebook 的工程师们给 React 带来一系列的新的特性,如 suspense 和 time slicing 等,这些都为 React 接下来即将到来的异步渲染机制作准备,有兴趣的能够看 Sophie Alpert 在 JSConf Iceland 2018 的演讲javascript

像 time slicing 等 React 内部优化特性,在 API 层面不会有太大变化,而 API 层面最大的变化,应该在生命周期钩子。html

React 的生命周期 API 一直以来十分稳定,可是当 React 团队在引入异步渲染机制的时候,发现以前的生命周期会的使用产生一些问题,因此才会改动生命周期 API,感兴趣的能够看这篇博客java

在 React 16.3 中,为下面三个生命周期钩子加上了 UNSAFE 标记:react

  • UNSAFE_componentWillMount
  • UNSAFE_componentWillReceiveProps
  • UNSAFE_componentWillUpdate

新增了下面两个生命周期方法:git

  • static getDerivedStateFromProps
  • getSnapshotBeforeUpdate

在目前16.X(X>3)的 React 中,使用 componentWillMount, componentWillReceiveProps, and componentWillUpdate 这三个方法会收到警告。React 团队计划在 17.0 中测地废弃掉这几个 APIgithub

新的生命周期钩子: static getDerivedStateFromProps

class Example extends React.Component {
  static getDerivedStateFromProps(props, state) {
    // ...
  }
}
复制代码

React 在实例化组件以后以及从新渲染组件以前,将调用新的静态 getDerivedStateFromProps 生命周期方法。该方法相似于 componentWillReceiveProps,能够用来控制 props 更新 state 的过程。它返回一个对象表示新的 state。若是不须要更新组件,返回 null 便可。markdown

getDerivedStateFromProps 与 componentDidUpdate一块儿将会替换掉全部的 componentWillReceiveProps。网络

新的生命周期钩子: getSnapshotBeforeUpdate

class Example extends React.Component {
  getSnapshotBeforeUpdate(prevProps, prevState) {
    // ...
  }
}
复制代码

getSnapshotBeforeUpdate 方法在 React 对视图作出实际改动(如 DOM 更新)发生前被调用,返回值将做为 componentDidUpdate 的第三个参数。less

getSnapshotBeforeUpdate 配合 componentDidUpdate 能够取代 componentWillUpdate。异步

为什么移除 componentWillMount

由于在 React 将来的版本中,异步渲染机制可能会致使单个组件实例能够屡次调用该方法。不少开发者目前会将事件绑定、异步请求等写在 componentWillMount 中,一旦异步渲染时 componentWillMount 被屡次调用,将会致使:

  • 进行重复的时间监听,没法正常取消重复的 Listener,更有可能致使内存泄漏
  • 发出重复的异步网络请求,致使 IO 资源被浪费
  • 在服务端渲染时,componentWillMount 会被调用,可是会因忽略异步获取的数据而浪费 IO 资源

如今,React 推荐将本来在 componentWillMount 中的网络请求移到 componentDidMount 中。至于这样会不会致使请求被延迟发出影响用户体验,React 团队是这么解释的:

There is a common misconception that fetching in componentWillMount lets you avoid the first empty rendering state. In practice this was never true because React has always executed render immediately after componentWillMount. If the data is not available by the time componentWillMount fires, the first render will still show a loading state regardless of where you initiate the fetch. This is why moving the fetch to componentDidMount has no perceptible effect in the vast majority of cases.

componentWillMount、render 和 componentDidMount 方法虽然存在调用前后顺序,但在大多数状况下,几乎都是在很短的时间内前后执行完毕,几乎不会对用户体验产生影响。

为什么移除 componentWillUpdate

大多数开发者使用 componentWillUpdate 的场景是配合 componentDidUpdate,分别获取 rerender 先后的视图状态,进行必要的处理。但随着 React 新的 suspense、time slicing、异步渲染等机制的到来,render 过程能够被分割成屡次完成,还能够被暂停甚至回溯,这致使 componentWillUpdate 和 componentDidUpdate 执行先后可能会间隔很长时间,足够使用户进行交互操做更改当前组件的状态,这样可能会致使难以追踪的 BUG。

React 新增的 getSnapshotBeforeUpdate 方法就是为了解决上述问题,由于 getSnapshotBeforeUpdate 方法是在 componentWillUpdate 后(若是存在的话),在 React 真正更改 DOM 前调用的,它获取到组件状态信息更加可靠。

除此以外,getSnapshotBeforeUpdate 还有一个十分明显的好处:它调用的结果会做为第三个参数传入 componentDidUpdate,避免了 componentWillUpdate 和 componentDidUpdate 配合使用时将组件临时的状态数据存在组件实例上浪费内存,getSnapshotBeforeUpdate 返回的数据在 componentDidUpdate 中用完即被销毁,效率更高。

总结

React 近来 API 变化十分大,React 团队很长时间以来一直在实现异步渲染机制,目前的特性只是为异步渲染作准备,预计 React 在 17 版本发布时,性能会取得巨大的提高,期待中。。。

PS:从 Sophie Alpert 演示的两个 DEMO 上看,异步渲染的高效确实十分惊艳,有兴趣的能够看文章开头的演讲。

个人全部文章都汇总在个人 GitHub: HuQingyang (胡青杨) · GitHub ,欢迎关注、交流、拍砖、搞基

相关文章
相关标签/搜索