新的生命周期函数是怎样的
React生命周期分为三个阶段:挂载、更新、卸载,React16后又多了一个异常,我们一一看下。
[更新图片,此处特别感谢 @wuzhengyan2015 指正]
挂载
生命周期的执行顺序
constructor
static getDerivedStateFromProps
render
componentDidMount
render
和componentDidMount
较 React16 之前无变化。对于挂载过程,我们着重看下constructor
、componentWillMount
和static getDerivedStateFromProps
。
constructor
- 初始化 state
注意:应避免使用props
给state
赋值,这样的话,state
的初始化可以提到constructor
外面处理
constructor(props) {
super(props);
this.state = {
x: 1,
// y: props.y, // 避免这样做,后面我们会讲应该怎样处理
}
}
复制代码
- 给方法绑定this
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
复制代码
但是,以上两件事放到constructor
外面处理会更简单些,如下:
class C extends React.Component {
state = {
x: 1
}
handleClick = (e) => {
// xxx
}
}
复制代码
所以,React16 以后用到constructor
的场景会变少。
componentWillMount
可以看到,componentWillMount
在 React16 中被“删掉”了(这样说其实是有问题的,因为 React 并未真正删除该生命周期函数,只是告诫开发者,该函数在未来版本中会被废弃掉),那么问题就出现了,原先在这个生命周期中的做的事情,现在该放到哪里去做呢?
首先问自己一个问题,原先的时候都在这个生命周期里做什么?答案是大部分时候会在这里做 AJAX 请求,然后执行setState
重新渲染。
然而在componentWillMount
里做 AJAX 请求实在不是一个明智之举,因为对于同构项目中,componentWillMount
是会被调用的。
还有人会在这里面初始化state
,关于state
的初始化,请参看楼上小节。
综上所述,componentWillMount
其实本来没有什么主要作用,如果你的代码规范,去掉的话,不会对现在的项目产生什么影响。
static getDerivedStateFromProps
上面我们讲到,应避免使用props
给state
赋值,但是在 React16 前我们都是这么做的,现在如果不让这么操作了,那该在哪里处理这块逻辑呢? React16 给出的答案就是 static getDerivedStateFromProps
。
挂载组件时,该静态方法会在render
前执行;更新组件时,该静态方法会在shouldComponentUpdate
前执行。
class C extends React.Component {
state = {
y: 0
}
static getDerivedStateFromProps(props, state): State {
if(props.y !== state.y) {
return {
y: props.y
};
}
}
}
复制代码
getDerivedStateFromProps
的返回值将作为setState
的参数,如果返回null,则不更新state,不能返回object 或 null 以外的值,否则会警告。
getDerivedStateFromProps
是一个静态方法,是拿不到实例this
的,所以开发者应该将该函数设计成纯函数。
这样,有没有发现componentWillReceiveProps
也就没有用武之地了?是的,React16 把它也“删掉”了(这样说其实是有问题的,因为 react 并未真正删除该生命周期函数,只是告诫开发者,该函数在未来版本中会被废弃掉,建议使用更好的getSnapshotBeforeUpdate
或 getDerivedStateFromProps
)
更新
生命周期函数的执行顺序
static getDerivedStateFromProps
shouldComponentUpdate
render
getSnapshotBeforeUpdate
componentDidUpdate
static getDerivedStateFromProps
前面已经介绍过了,而其他的几个生命周期函数与 React16 之前基本无异,所以这里主要介绍下getSnapshotBeforeUpdate
。
getSnapshotBeforeUpdate
在 React 更新 DOM 之前调用,此时state
已更新; 返回值作为componentDidUpdate
的第3个参数; 一般用于获取render
之前的 DOM 数据
语法:
class C extends React.Component {
getSnapshotBeforeUpdate (prevProps, prevState): Snapshot {
}
componentDidUpdate(prevProps, prevState, snapshot) {
// snapshot 是从 getSnapshotBeforeUpdate 的返回值,默认是 null
}
}
复制代码
getSnapshotBeforeUpdate
的使用场景一般是获取组建更新之前的滚动条位置。
卸载
componentWillUnmount
较之前无变化。
异常
componentDidCatch
这个函数是 React16 新增的,用于捕获组件树的异常,如果render()
函数抛出错误,则会触发该函数。可以按照 try catch
来理解和使用,在可能出现错误的地方,使用封装好的包含 componentDidCatch
生命周期的组建包裹可能出错的组件。
class PotentialError extends React.Component {
state = {
error: false,
}
componentDidCatch(error, info) {
console.error(info);
this.setState({
error
});
}
render() {
if (this.state.error) {
return <h1>出错了,请打卡控制台查看详细错误!</h1>;
}
return this.props.children;
}
}
复制代码
如:
const Demo = () => (
<PotentialError>
<div>{{a: 1}}</div>
</PotentialError>
)
复制代码
这样,Demo
组件即使直接使用对象作为子组件也不会报错了,因为被 PotentialError
接收了。
新生命周期的完整demo
看看穿上新生命周期这身新衣服后的样子吧
import React from 'react'
export default class MyComponent extends React.Component {
constructor(props) {
super(props);
// 初始化state方式(1)
this.state = {
}
}
static defaultProps = {
}
// 初始化state方式(2)
state = {
}
static getDerivedStateFromProps(props, state) {
return state
}
componentDidCatch(error, info) {
}
render() {
}
componentDidMount() {
}
shouldComponentUpdate(nextProps, nextState) {
}
getSnapshotBeforeUpdate(prevProps, prevState) {
}
componentDidUpdate(prevProps, prevState, snapshot) {
}
componentWillUnmount() {
}
}