react V16.4 生命周期图示:

111.png

这里的不常使用意思是大多数组件可能都不需要去使用这个方法,但是有些时候使用确实会提到一定的作用。

一、shouldComponentUpdate

shouldComponentUpdate(nextProps, nextState) 能够直接决定 React 的组件是否进行更新,无论是与当前的 props 还是与当前的 state 进行比较,最终总是会得出一个 true 或者是 false,来决定组件是否更新。

默认当然是组件都会进行更新,因此一般来说我们都可以直接依赖默认行为进行组件的更新。

具体的处理流程是 在接收新的 props 或者是 state 时,在重新 render 之前会调用 shouldComponentUpdate 方法,默认是返回 true,如果 什么都不返回,则会当做 false 处理

在初始化渲染或者是调用 forceUpdate 方法的时候不调用这个方法。

shouldComponentUpdate 方法只是能够用来优化性能,不能过度依赖 shouldComponentUpdate 来阻止组件的渲染,因为可能会导致 bug

如果要阻止重新渲染的话,可以考虑使用 React.PureComponent, 而不是手动重写 shouldComponentUpdate

React.PureComponent 会对 props 和 state 进行浅比较,然后决定时候需要进行刷新,所以并不需要自己进行更新。

需要注意的是,即使 shouldComponent 返回了 false,子组件的状态更新引起的重新渲染是不会受到影响的

shouldComponentUpdate 中非常不建议进行深层比较,或者是使用 JSON.stringify,因为效率很低,而且很容易引起性能问题。

通常来说,如果 shouldComponentUpdate 返回 false,则不会调用 UNSAFE_componentWillUpdaterendercomponentDidUpdate 这几个方法。

未来的计划中提到,会将 shouldComponentUpdate 作为一种提示性质作用,而不是绝对的严格执行的指令,也就是说,即使返回了 false,也可能会导致组件的重新渲染

二、getDerivedStateFromProps

从 props 中派生 state 是永远不推荐的

无论是在初始安装还是在后面的 update 中。render 方法之前会执行 getDerivedStateFromProps 方法, 这个方法应该返回一个对象来更新 state,或者返回 null,什么也不进行更新。

getDriverdStateFromProps 用于罕见的几种用例,这些用例中, state 可能会取决于 props 随着时间的变化而变化。比如,我们能够轻松地实现一个 <Transition> 组件,这个组件可以比较前后两个子组件,从而决定哪个子组件过渡进入或者是过渡消失。

派生 state 会导致代码冗长,并且会使得组件变的难以理解。

关于不推荐使用 派生 state 方案的 react 博客地址:

一般来说,当你要使用派生 state 的时候,首先应该考虑替代方案:

  • 如果需要响应 props 的变动,并且进行一次响应式的操作,比如请求数据等,应当考虑在 shouldComponentUpdate 中进行。
  • 如果只想在 props 改变的时候重新计算一些数据,应当考虑使用 memoization helper
  • 如果想要在 props 更改的时候,重置 一些 state,应当考虑使用完全可控组件带key的非受控组件

getDerivedStateFromProps 是无权访问组件实例的。不过,可以通过在类定义之外提取组件的 props 和状态的纯函数,在 getDerivedStateFromPorps 和其他类方法之间重用一些代码。

需要注意的是,无论是什么原因,都会在每次 render 中触发这个方法,这和 UNSAFE_componentWillReceiveProps 是有鲜明的对比的, UNSAFE_componentWillReceiveProps 仅在父组件引起的重新渲染而不是因为本地 setState 更新引起的渲染结果时触发。

三、getSnapshotBeforeUpdate

getSnapshotBeforeUpdate(prevProps, prevState) 在最近一次的 render 完将要 commit 给 DOM 的时候会调用,这个方法能够使得组件可以在可能更改之前从 DOM 捕获一些信息,比如滚动的位置等等。这个方法返回的任何值,都会传递给 componentDidUpdate(nextProps, nextState, snapshot)

这个方法一般来说是不会使用的,不过它可能会出现在需要以特殊方式进行处理UI,比如像是聊天线程中处理滚动位置。

getSnapshotBeforeUpdate() 必须返回一个值或者是返回 null

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

  getSnapshotBeforeUpdate(prevProps, prevState) {
    // 捕获滚动的位置,以便后面进行滚动 注意返回的值
    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 会进行滚动的调整,这样子就不会立即将之前的内容直接弹上去
    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 阶段(比如 getSnapshotBeforeUpdatecomponentDidUpdate) 中读取可能存在延迟。