# Lifecycle Methods

V16.3版本的react对组件的生命周期函数进行了一些修改
对于任何一个框架,或者组件而言,都有其生命周期的定义,比如从定义到销毁,会有一定的流程在其中控制。

生命周期就是指一个对象的生老病死。 生命周期(Life Cycle)的概念应用在各行各业中。广义上来说泛指自然界和人类社会各种客观事物的阶段性变化及其规律。自然界的生命周期,可以划分为出生、生长、成熟、衰老、死亡。不同体系中的生命周期都是从上述规律中演变而来,运用到了软件开发这个行业中。

React组件的生命周期分为三个阶段:初始化、运行中和销毁这三个阶段。开始使用组件初始化,组件需要更新 运行中,组件不需要用销毁。React在每个生命周期阶段都给我们提供了对应的钩子函数,以此来完成一些更高级的组件。

# 组件生命周期(V16.4)

参考: http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/

# 初始化阶段执行

当组件实例被创建并插入 DOM 中时,其生命周期调用顺序如下:

序号 方法 重要性 描述
1 static defaultProps stars 获取默认属性,只调用一次(创建类的时候有且调用一次)
2 constructor stars react组件的构造函数在挂载之前被调用。
3 static getDerivedStateFromProps(nextProps, prevState) stars 在组件实例化后,接受新的props后,状态修改后会被调用。
4 render() stars ** 唯一必须添加的方法 ** ,被调用时,组件在render函数生成虚拟节点,最后由react将虚拟节点变成真正的节点渲染到页面上
5 componentDidMount() stars 组件被装载后才会被调用

# constructor

如果不初始化 state 或不进行方法绑定,则不需要为 React 组件实现构造函数。

在 React 组件挂载之前,会调用它的构造函数。在为 React.Component 子类实现构造函数时,应在其他语句之前前调用 super(props)。否则,this.props 在构造函数中可能会出现未定义的 bug。

通常,在 React 中,构造函数仅用于以下两种情况:

  • 通过给 this.state 赋值对象来初始化内部 state。
  • 为事件处理函数绑定实例
class Test extends React.Component{
    constructor(props){
        super(props);

        //初始化内部 state
        this.state = {};

        //为事件处理函数绑定实例 修复事件处理程序this指向
        this.handleChange = this.handleChange.bind(this);
    }
}

注意: 在 constructor() 函数中不要调用 setState() 方法。如果你的组件需要使用内部 state,请直接在构造函数中为 this.state 赋值初始 state:

# static getDerivedStateFromProps(nextProps, prevState)

这个方法将会在组件实例化和接收到新的 props 的时候被调用。可以看出,当组件实例化的时候,这个方法替代了componentWillMount(),而当接收到新的 props 时,该方法替代了 componentWillReceiveProps() 和 componentWillUpdate()。

注意: 这个方法是个 static 的方法,因此使用 this 在这个方法中并不指代本组件,如果打印出来会发现这个this是null。 而且这个方法有返回值。当需要更新状态时,需要返回一个state object(没有新增该状态,有更改指定状态),如果不需要任何更新,则返回null即可。

# render()

render() 方法是 class 组件中唯一必须实现的方法。当 render 被调用时,它会检查 this.props 和 this.state 的变化并返回以下类型之一:

  • React 元素。通常通过 JSX 创建。例如,<div /> 会被 React 渲染为 DOM 节点,<MyComponent /> 会被 React 渲染为自定义组件,无论是 <div /> 还是 <MyComponent /> 均为 React 元素。
  • 数组或 fragments。 使得 render 方法可以返回多个元素。欲了解更多详细信息,请参阅 fragments 文档。
  • Portals。可以渲染子节点到不同的 DOM 子树中。欲了解更多详细信息,请参阅有关 portals 的文档
  • 字符串或数值类型。它们在 DOM 中会被渲染为文本节点
  • 布尔类型或 null。什么都不渲染。(主要用于支持返回 test && 的模式,其中 test 为布尔类型。)

render() 函数应该为纯函数,这意味着在不修改组件 state 的情况下,每次调用时都返回相同的结果,并且它不会直接与浏览器交互。

# componentDidMount()

componentDidMount() 会在组件挂载后(插入 DOM 树中)立即调用。依赖于 DOM 节点的初始化应该放在这里。如需通过网络请求获取数据,此处是实例化请求的好地方。

由于这个方法发生在render()之后,因此在这个方法中调用setState()会导致一次额外的渲染,只不过这次渲染会发生在浏览器更新屏幕之前。因此即使渲染了两次,用户也不会看到中间状态,即不会有那种状态突然跳一下的情况发生。对于像modals和tooltips这种需要在渲染前知道尺寸大小、位置的情况来说,也是很有用。只不过,虽然在用户视觉体验上可能没有影响,但是这种操作可能会导致性能方面的问题,因此还需慎用。

# 运行中阶段执行

当组件的 props 或 state 发生变化时会触发组件的更新,组件重新渲染的过程中生命周期调用顺序如下:

序号 方法 重要性 描述
1 static getDerivedStateFromProps(nextProps, prevState) stars 在组件实例化后,接受新的props后,状态修改后会被调用。
2 shouldComponentUpdate() stars 返回一个布尔值。在组件接收到新的props或者state时被调用。 可以在你确认不需要更新组件时使用。(nextProps,nextState)
3 render() stars ** 唯一必须添加的方法 ** ,组件在render函数生成虚拟节点,最后由react将虚拟节点变成真正的节点渲染到页面上
4 getSnapshotBeforeUpdate(prevProps, prevState) stars 在更新之前被调用(不常用)
5 componentDidUpdate() stars 真正的DOM被渲染之后调用

# shouldComponentUpdate

根据 shouldComponentUpdate() 的返回值,判断 React 组件的输出是否受当前 state 或 props 更改的影响。默认行为是 state 每次发生变化组件都会重新渲染。大部分情况下,你应该遵循默认行为。

当 props 或 state 发生变化时,shouldComponentUpdate() 会在渲染执行之前被调用。返回值默认为 true。首次渲染或使用 forceUpdate() 时不会调用该方法。

class Test extends React.Component{
    shouldComponentUpdate(nextProps, nextState)
        //other code
        return true|false;
    }   
}

# componentDidUpdate

当组件更新后,可以在此处对 DOM 进行操作。如果你对更新前后的 props 进行了比较,也可以选择在此处进行网络请求。(例如,当 props 未发生变化时,则不会执行网络请求)。

componentDidUpdate(prevProps) {
  // 典型用法(不要忘记比较 props):
  if (this.props.userID !== prevProps.userID) {
    this.fetchData(this.props.userID);
  }
}

# 卸载阶段

序号 方法 重要性 描述
1 componentWillUnmount stars 这个函数在销毁操作真正执行之前调用,给开发者最后的机会进行一些清理工作

componentWillUnmount() 会在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,例如,清除 timer,取消网络请求或清除在 componentDidMount() 中创建的订阅等。

class App extends React.Component{
    componentWillUnmount(){
        //组件将要卸载的调用
    }
    render(){
        return (<div>Hello!<div>)
    }
}

在componentWillUnmount方法中,我们通常会执行一些清理操作,例如:移除事件处理程序或者清除定时器。

# 生命周期功能替换一览

class Test extends React.Component{
    初始化属性
    static defaultProps = {}

    constructor(props) {
        super(props);
        1. 初始化状态
        this.state = {}
        2. 修改事件处理程序 this指向
    }

    static getDerivedStateFromProps(nextProps, prevState) {
        1. 基于属性更新状态
        2. 属性变化时获取外部数据
        return null | {}
    }

    render() {
    }

    componentDidMount(){
        1. DOM操作
        2. 从服务器获取数据
    }

    shouldComponentUpdate() {
        决定组件是否更新
        return ture|false
    }

    getSnapshotBeforeUpdate(prevProps, prevState) {
        在更新之前读取DOM属性
        类似于快照 'snapshot' 为传递给componentDidUpdate 的 snapshot 参数

        return snapshot
    }

    componentDidUpdate(prevProps, prevState, snapshot) {
        snapshot 为上一个生命周期 getSnapshotBeforeUpdate return 的参数
    }

    componentWillUnmount() {
        1. 做一些清理工作,如事件绑定、定时器
    }
}