目前 React 16.8+的生命周期分为三个阶段,分别是挂载阶段、更新阶段、卸载阶段。分别对应组件生命周期的三个状态:1.Mounting:已插入真实DOM;2.Updating:正在被重新渲染;3.Unmounting:已移出真实DOM
React 的生命周期的函数有哪些:
- 组件将要挂载时触发的函数:componentWillMount
- 组件挂载完成时触发的函数:componentDidMount
- 是否更新数据时触发的函数:shouldComponentUpdate
- 将要更新数据时触发的函数:componentWillUpdate
- 数据更新完成时触发的函数:componentDidUpdate
- 组件将要销毁时触发的函数:componentWillUnmount
- 父组件中改变了props传值时触发的函数:componentWillReceiveProps
其中在React 16之后 componentWillMount、componentWillReceiveProps、componentWillUpdate这三个生命周期函数将会被废弃,请使用新增的生命周期函数。
挂载阶段:
当组件实例被创建并插入 DOM
中时,被称为组件挂载
。其生命周期调用顺序如下:
constructor(props) — static getDerivedStateFromProps() — UNSAFE_componentWillMount() — render() — componentDidMount()
constructor
:构造函数,最先被执行,我们通常在构造函数里初始化 state 对象或者给自定义方法绑定 this。在 React 组件挂载之前会调用它的构造函数。在为 React.Component
子类实现构造函数时,应在其他语句之前调用 super(props)
。否则,this.props 在构造函数中可能会出现未定义的 bug。
通常在 React 中,构造函数仅用于以下两种情况:1.通过 this.setState 赋值给对象来初始化内部 state。2.为事件处理函数绑定实例。如果不初始化 state 且不进行方法绑定,则不需要为 React 组件实现构造函数。
static getDerivedStateFromProps
:这是个静态方法。会在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。它应会返回一个对象来更新 state,如果返回 null 则不更新任何内容。此方法无权组件实例。如果你需要,可以通过提取组件 props 的纯函数及 class 之外的状态,在 getDerivedStateFromProps 和其他 class 方法之间重用代码。
render
:是 class 组件中唯一一个必须实现的方法。该函数应该为纯函数,这意味在不修改 state 的情况下,每次调用时都返回同样的结果,并且它不会直接与浏览器交互。如需与浏览器进行交互,请在 componentDieMount() 或其他生命周期方法中执行你的操作。保持 render() 为纯函数,可以使组件更容易思考。
componentDidMount
:组件装载之后调用,此时我们可以获取 DOM 节点并操作,比如对 canvas、svg 的操作,服务器请求,订阅都可以写在这里面,但是记得在componentWillUnmount中取消订阅。此组件会在组件挂载后(插入 DOM 树中)立即调用。依赖于 DOM 节点的初始化应该放在这里。如需通过网络请求获取数据,此处是实例化请求的好地方。
更新阶段:
当组件的 props 或 state 发生变化时会触发更新。组件更新的生命周期调用顺序如下:
static getDerivedStateFromProps() — shouldComponentUpdate() — UNSAFE_componentWillReceiveProps() — UNSAFE_componentWillUpdate() — render() — getSnapshotBeforeUpdate() — componentDidUpdate()
getDerivedStateFromProps
:此方法在更新挂载阶段都可能会调用
shouldComponentUpdate
:shouldComponentUpdate(nextProps,nextState),该方法是控制组件是否应该被更新。如果方法返回 false 则后续的生命周期方法将全部跳过,该方法会接收 nextProps 和 nextState,可以通过比较this.props 和 nextProps、this.state 和 nextState来决定是否要重新渲染组件,来对组件进行性能优化。你应该考虑使用内置的 PureComponent 组件,而不是手动编写 shouldComponentUpdate()。
官网中不建议在 shouldComponentUpdate() 中进行深层比较或使用 JSON.Stringfy()。这样非常影响效率,且会损坏性能。
render
:更新阶段也会触发此声明周期
getSnapshotBeforeUpdate(preProps,preState)
:在最近一次渲染输出(提交的到DOM 节点)之前调用。它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)。此生命周期的任何返回值将作为参数传递给 componentDidUpdate()。
componentDidUpdate(preProps,preState,snapShot)
:此函数会在更新后被立即调用。首次渲染不会执行此方法。
当组件更新后,可以在此处对 DOM 进行操作。如果你对更新前后的 poprs 进行了比较,也可以在此处进行网络请求。(例如,当props未发生变化时,则不会执行网络请求。)
componentDidUpdate(prevProps) {
// 典型用法(不要忘记比较 props):
if (this.props.userID !== prevProps.userID) {
this.fetchData(this.props.userID);
}
}
你也可以在 componentDidUpdate() 中直接调用 setState() ,但请注意它必须被包裹在一个条件语句中,正如上面的例子那样进行处理,否则会导致死循环。
卸载阶段:
componentWillUnmount
:会在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,例如,清除定时器,取消网络请求或清除在 componentDidMount() 中创建的订阅,清理无效的 DOM 元素等垃圾清理工作。
异常处理:
static getDerivedStateFromError(error) {
// 更新 state 使下一次渲染可以显示降级 UI
return { hasError: true };
}
static getDerivedStateFromError
:此生命周期会在渲染阶段后代组件抛出错误后被调用。它将抛出的错误作为参数,并返回一个值以更新 state。你可以通过判断 state 中的某个字段,来显示降级 UI.
componentDidCatch:此生命周期会在后代组件抛出错误后被调用,可以用于日志记录。它接收两个参数:1.error:抛出的错误,2.info:带有 componentStack key 的对象,其中包含有关组件引发错误的栈信息。componentDidCatch 会在“提交”阶段被调用,因此允许执行副作用。它应该用于记录错误之类的情况。
componentDidCatch(error, info) {
logToMyService(info.componentStack);
}