#

用惯了 vue 用 react 的生命周期总有一丝怪异啊,不过这并不妨碍我记点笔记

生命周期仅存在于类组件中,函数组件每次调用都是重新运行函数,旧的组件会被立刻销毁

# 旧版生命周期

# 总览

旧版生命周期对应 React 的版本小于 16.3
file

# constructor(props, context, updater)

React 借用 class 类的 constructor 充当初始化钩子。

React 规定 constructor 有三个参数,分别是 props、context 和 updater。

  • props 是属性,它是不可变的。
  • context 是上下文
  • updater 是包含一些更新方法的对象,this.setState 最终调用的是 this.updater.enqueueSetState 方法,this.forceUpdate 最终调用的是 this.updater.enqueueForceUpdate 方法,所以这些 API 更多是 React 内部使用,暴露出来是以备开发者不时之需。

在 React 中,constructor 需要先调用 super (props),用于初始化 this 和 props,详情可以看这里

constructor 生命周期钩子中可以初始化 this.state。

也可以使用下面的代码初始化 state

import React, { Component } from 'react';
class App extends Component {
    state = {
        name: 'biu',
    };
}
export default App;

constructor 只会在组件创建时调用一次,之后的组件更新不会再调用 constructor

# componentWillMount()

这是组件挂载到 DOM 之前的生命周期钩子。

在某些特殊情况下,这个函数有可能会被调用两次,

  • 服务端渲染
  • Fiber 的 Reconciliation 过程中打断了这个生命周期

所以有可能出现一些 bug,不过这个生命周期好像也没什么用,所以建议尽量不要使用

# render()

作为一个组件,最核心的功能就是把元素挂载到 DOM 上,所以 render 生命周期钩子是一定会用到的。

render 会返回一个虚拟 DOM,这个虚拟 DOM 会被挂载到虚拟 DOM 树中,最终渲染到页面的真实 DOM 中

render 可能不只运行一次,只要需要重新渲染,就会重新运行

在 render 中最好不要使用 setState,因为可能会导致无限递归渲染

# componentDidMount()

这是组件挂载到 DOM 之后的生命周期钩子,这个时候的组件已经就绪,你可以随心所欲的操作数据了

通常情况下,我们会将网络请求、启动计时器等初始化操作,书写到该函数中

# componentWillReceiveProps(nextProps)

componentWillReceiveProps 生命周期钩子只有一个参数,即更新后的 props,这个生命周期不会在组件初始化时触发

该声明周期函数可能在两种情况下被触发:

  • 组件接收到了新的属性。
  • 组件没有收到新的属性,但是由于父组件重新渲染导致当前组件也被重新渲染。

同样,因为 Fiber 机制的引入,这个生命周期钩子有可能会多次触发。

# shouldComponentUpdate(nextProps, nextState)

这个生命周期钩子用于指示 React 是否要重新渲染该组件 (通过返回 true 或者 false), 主要用于性能优化

shouldComponentUpdate 生命周期钩子默认返回 true。也就是说,默认情况下,只要组件触发了更新,组件就一定会更新。

如果开发者调用 this.forceUpdate 强制更新,React 组件会无视这个钩子

# componentWillUpdate(nextProps, nextState)

组件即将被重新渲染时调用这个钩子,具体来说是在 shouldComponentUpdate 生命周期钩子返回 true,或者调用 this.forceUpdate 之后。

在 componentWillUpdate 中最好不要使用 setState,因为可能会导致无限递归渲染

同样,因为 Fiber 机制的引入,这个生命周期钩子有可能会多次调用。

# componentDidUpdate(nextProps, nextState)

这是组件更新之后触发的生命周期钩子,在这里可以拿到更新后的 DOM

在 componentDidUpdate 中最好不要使用 setState,因为可能会导致无限递归渲染

# componentWillUnmount()

这是组件卸载之前的生命周期钩子,通常在该函数中销毁一些组件依赖的资源,比如计时器或者注册的全局事件

# 新版生命周期

# 总览

file

# 删除的 API

  • componentWillMount
  • componentWillUpdate
  • componentWillReceiveProps

旧版的生命周期函数非常的对称,有 componentWilUpdate 对应 componentDidUpdate,有 componentWillMount 对应 componentDidMount;也考虑到了因为父组件引发渲染可能要根据 props 更新 state 的需要,所以有 componentWillReceiveProps。

但是,这个生命周期函数的组合在 Fiber 之后就显得不合适了,如果开启 async rendering,在 render 函数之前的所有函数,都有可能被执行多次。所以为了安全考虑,react 废弃了这三个 API

# static getDerivedStateFromProps(props, state)

getDerivedStateFromProps 是一个静态函数,所以函数体内不能访问 this,简单说,就是一个纯函数

static getDerivedStateFromProps(nextProps, prevState) {
	// 根据 nextProps 和 prevState 计算出预期的状态改变,返回结果会被送给 setState
}

getDerivedStateFromProps 是 componentWillReceiveProps 的替代品,但是前者比后者更安全,因为不能访问 this,就可以避免开发者做很多预料之外的操作,因为这个 hook 的设计初衷是根据 props 更新 state

# getSnapshotBeforeUpdate()

这函数会在 render 之后执行,而执行之时 DOM 元素还没有被更新,开发者可以在这时获取 dom 的一些信息 (比如保存网页滚动条位置)

函数的返回值会作为 componentDidUpdate 的第三个参数传入

getSnapshotBeforeUpdate(prevProps, prevState) {
    console.log('#enter getSnapshotBeforeUpdate');
    return 'foo';
  }
  componentDidUpdate(prevProps, prevState, snapshot) {
    console.log('#enter componentDidUpdate snapshot = ', snapshot);
  }

# 参考

React 生命周期

React v16.3 之后的组件生命周期函数