react的生命周期

149 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情

生命周期

生命周期(left cycle)就是指一个对象的生老病死。基本涵义可以通俗理解为从“摇篮到坟墓”(cradle-to-grave)的整个过程。

每个组件都包含 “生命周期方法”,你可以重写这些方法,以便于在运行过程中特定的阶段执行这些方法。 react官网

每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。vue官网

react的生命周期从15到16.3(2018年3月29号)有很多的变化,而17,18就没有啥变化了。

React15React16
挂载时constructor -> componentWillMount -> render -> componentDidMountconstructor -> getDerivedStateFromProps -> render -> componentDidMount
更新时componentWillReceiveProps(父更新) -> shouldComponentUpdate(自己更新) -> componentWillUpdate -> render -> compoentDidUpdategetDerivedStateFromProps -> shouldComponentUpdate -> render -> getSnapshotBeforeUpdate -> componentDidUpdate
卸载时componentWillUnmountcomponentWillUnmountcomponentWillUnmount

要点

16.3添加了两个钩子,同时给标记三个钩子为不安全的。

添加的钩子为:getDerivedStateFromProps,getSnapshotBeforeUpdate 。同时给componentWillMount, componentWillUpdate, componentWillReceiveProps 添加UNSAFE_别名。就是如果使用它们会明确第看到警告信息。 17.0 彻底移除上面的三个钩子。 参考

React16开始慢慢从生命周期中去掉了componentWillMoutcomponentWillUpdate方法,并且使用getDerivedStateFromProps方法替代了之前的componentWillReceiveprops。 原因是:

  1. 用户经常会在这个三个钩子中嵌入错误的业务代码,导致异常。为了减少这类错误的使用,直接删除它们
  2. 给Fiber机制的引入打下基础。移除的三个钩子都有关键字will,他们都是在render()之前完成的。在Fiber架构下,又将生命周期划分了如下三个阶段:
  • (1)render阶段:纯净且没有副作用,可能会被暂停或者终止,重新启动。getDerivedStateFromProps -> shouldComponentUpdate -> render
  • (2)Pre-commit阶段:可以读取DOM。getSnapshotBeforeUpdate()
  • (3)commit阶段:可以使用DOM,运行副作用,安排更新。componentDidUpdate() 在Fiber架构下,render阶段是可能会被React暂停,终止或者重新启动的。如果这三个钩子还处在其中,会导致更大的维护风险。

getDerivedStateFromProps

它是一个比较特别的钩子:

  1. 唯一个静态的方法:要在前面加static,且内部无法拿到组件实例。
  2. 父组件触发导致的更新,它的执行顺序是:getDerivedStateFromProps -> shouldComponentUpdate -> render。
  3. 返回一个对象来更新 state,如果返回 null 则不更新任何内容。 参考

componentWillUnmount

componentWillUnmount() 会在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,例如:

  1. 清除 timer
  2. 取消网络请求
  3. 清除在 componentDidMount() 中创建的事件监听

不应调用 setState(),因为该组件将永远不会重新渲染。组件实例卸载后,将永远不会再挂载它。

getSnapshotBeforeUpdate()

getSnapshotBeforeUpdate(prevProps, prevState)

getSnapshotBeforeUpdate() 在componentDidUpdate之前调用。它任何返回值将作为参数传递给 componentDidUpdate()

它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)。此用法并不常见,但它可能出现在 UI 处理中,如需要以特殊方式处理滚动位置的聊天线程等。

class ScrollingList extends React.Component {
  constructor(props) {
    super(props);
    this.listRef = React.createRef();
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    // 我们是否在 list 中添加新的 items ?
    // 捕获滚动位置以便我们稍后调整滚动位置。
    if (prevProps.list.length < this.props.list.length) {
      const list = this.listRef.current;
      return list.scrollHeight - list.scrollTop;
    }
    return null;
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    // 如果我们 snapshot 有值,说明我们刚刚添加了新的 items,
    // 调整滚动位置使得这些新 items 不会将旧的 items 推出视图。
    //(这里的 snapshot 是 getSnapshotBeforeUpdate 的返回值)
    if (snapshot !== null) {
      const list = this.listRef.current;
      list.scrollTop = list.scrollHeight - snapshot;
    }
  }

  render() {
    return (
      <div ref={this.listRef}>{/* ...contents... */}</div>
    );
  }
}

在上述示例中,重点是从 getSnapshotBeforeUpdate 读取 scrollHeight 属性,因为 “render” 阶段生命周期(如 render)和 “commit” 阶段生命周期(如 getSnapshotBeforeUpdate 和 componentDidUpdate)之间可能存在延迟。