为了更好的支持异步渲染(Async Rendering),解决一些生命周期滥用可能导致的问题,React 从 V16.3 开始,
对生命周期进行渐进式调整,还对生命周期加入了错误处理。
React 16.3
新增的生命周期方法
1. static getDerivedStateFromProps()
2. getSnapshotBeforeUpdate()
3. componentDidCatch()
逐渐废弃的生命周期方法:
1. componentWillMount()
2. componentWillReceiveProps()
3. componentWillUpdate()
调整前的生命周期:
图中被红框圈起来的三个生命周期函数就是在新版本中即将被移除的,在这三个生命周期函数中都可以去做一些诸如发送请求,setState
等包含副作用的事情。在老版本的
React
中,这样做也许只会带来一些性能上的损耗,但在React 开启异步渲染模式之后,就无法再接受这样的副作用产生了。
调整后的生命周期:
static getDerivedStateFromProps(nextProps, prevState)
当创建时、接收新的 props 时、setState 时、forceUpdate 时会执行这个方法。
注意:
v16.3 setState
时、
forceUpdate
时不会执行这个方法,
v16.4
修复了这个问题。
这是一个 静态方法,参数
nextProps
是新接收的
props
,
prevState
是当前的
state
。返回值(对象)将用于更新
state
,如果不需要更新则需要返回
null
。
eg:
这个方法的常用作用也很明显了:父组件传入新的
props
时,用来和当前的
state
对比,判断是否需要更新state。以前一般使用
componentWillReceiveProps
做这个操作。
这个方法建议尽量少用,只在必要的场景中使用,一般使用场景如下:
1.
无条件的根据
props
更新
state
2.
当
props
和
state
的不匹配情况更新
state
getSnapshotBeforeUpdate(prevProps, prevState)
这个方法在
render()
之后,
componentDidUpdate()
之前调用。
两个参数
prevProps
表示更新前的
props
,
prevState
表示更新前的
state
。
返回值称为一个快照(
snapshot
),如果不需要
snapshot
,则必须显示的返回
null ——
因为返回值将作为 componentDidUpdate() 的第三个参数使用。所以这个函数必须要配合
componentDidUpdate()
一起使用。
这个函数的作用是在真实
DOM
更新(
componentDidUpdate
)前,获取一些需要的信息(类似快照功能),然后作为参数传给 componentDidUpdate
。例如:在
getSnapShotBeforeUpdate
中获取滚动位置,然后作为参数传给 componentDidUpdate,就可以直接在渲染真实的
DOM
时就滚动到需要的位置。
eg:
错误处理 Error Handling
componentDidCatch(err, info)
任何子组件在渲染期间,生命周期方法中或者构造函数
constructor
发生错误时调用。
错误边界不会捕获下面的错误:
· 事件处理
(Event handlers)
(因为事件处理不发生在
React
渲染时,报错不影响渲染)
· 异步代码
(Asynchronous code) (e.g. setTimeout or requestAnimationFrame callbacks)
· 服务端渲染
(Server side rendering)
· 错误边界本身
(
而不是子组件
)
抛出的错误
componentDidUpdate(prevProps, prevState, snapshot) {
// If we have a snapshot value, we've just added new items.
// Adjust scroll so these new items don't push the old ones out of view.
// (snapshot here is the value returned from getSnapshotBeforeUpdate)
if (snapshot !== null) {
const list = this.listRef.current;
list.scrollTop = list.scrollHeight - snapshot;
}
}
render() {
return (
<div ref={this.listRef}>{/* ...contents... */}</div>
);
}
componentDidCatch(err, info)