上节课我们学习了关于getDerivedStateFromProps钩子的使用,也提到了这个钩子只作为了解即可,因为使用场景太少了。那么我们也就不在这周旋太久了,我们来看另一个新增的钩子getSnapshotBeforeUpdate

回顾

在开始这节课的内容之前,我们来对上节课做一个简单的回顾:

  • 仅当state值在任何时候都完全取决于props是才使用getDerivedStateFromProps钩子
  • 派生state会导致代码冗余且组件难以维护
  • 使用了派生state之后,将无法再正常通过state驱动页面更新

以上便是上节课的主要内容。

更新流程

我们先来看流程图,上节课我们说了一下挂载时那一块,和旧版生命周期唯一的区别是getDerivedStateFromProps钩子,而且我们也说了这个钩子应用场景罕见,只作了解,那么作为新增钩子中的另一个getSnapshotBeforeUpdate用得也不多。但是这个钩子和getDerivedStateFromProps又不太一样。那么我们接下来就看一下。

验证

首先这个钩子是什么在什么时候调用的?是不是在rendercomponentDidUpdate之间?而在旧的生命周期中这两个钩子之间是没有任何其他生命周期钩子在被调用的。

那么首先我们先来验证一下,这个调用顺序:

class Count extends React.Component {
  constructor(props) {...}

  componentDidMount() {...}
  componentWillUnmount() {...}
  getDerivedStateFromProps() {
    console.log("Count--getDerivedStateFromProps");
    return null;
  }
  shouldComponentUpdate() {
    console.log("Count--shouldComponentUpdate");
    return true;
  }
  getSnapshotBeforeUpdate() {console.log("Count--getSnapshotBeforeUpdate");}
  componentDidUpdate() {console.log("Count--componentDidUpdate");}

  render() {
    console.log("Count--render");
    ...
  }
  add = () => {...};
  unmount = () => {...};
  force = () => {...}
}

我们来看代码,我们并没有把getDerivedStateFromProps钩子给删掉,而是返回了null因为返回null的话是不会影响我们的state驱动页面更新的功能的。为什么我们不把这个钩子删掉呢?看流程图,因为这个钩子是更新流程的一个环节,我们为了完整展现整个流程,使用我们保留了这个钩子,只是返回了null使得我们的更新能够正常进行。

而且从流程图上面来看,我们如果需要通过state更新驱动页面更新,那么我们是不是要把shouldComponentUpdate钩子的返回值返回true才能正常往下进行?另外还有一点,如果我们写了getSnapshotBeforeUpdate钩子的话就一定要写componentDidUpdate钩子,否则:

image-20220104150228713

控制台中就会提示你说getSnapshotBeforeUpdate钩子应该和componentDidUpdate钩子一起使用,而你却只定义了getSnapshotBeforeUpdate。所以说componentDidUpdate钩子不能忘。那好我来看一下效果:

image-20220104150430725

我们来看,报错了,但是钩子的执行顺序没有任何问题。先是构造器,然后getDerivedStateFromProps钩子,热爱和render来挂载组件,挂载完了之后调用componentDidMount,当我们点击了Add按钮之后,触发setState,直接调用getDerivedStateFromProps钩子,然后到了阀门,判断是否允许组件更新,而我们的阀门一直是打开的,就会继续往下走,通过render更新组件,在执行完render之后执行了getSnapshotBeforeUpdate。从控制台中可以看出,这个钩子执行成功了,语法上是没有错误的。但是报了一个错误警告。当这个钩子执行完之后,调用了componentDidUpdate钩子。

首先来说,我吗成功验证了各个生命周期钩子的调用顺序与流程图完全一致。那么接下来我们来看看调用getSnapshotBeforeUpdate钩子报错是个什么情况?

报错信息说getSnapshotBeforeUpdate钩子必须返回一个snapshot值或者null,但是我们却返回了undefined。这个看着是不是和getDerivedStateFromProps钩子的要求还挺像的?关键我们不知道啥是snapshot值,那么我先保证不报错,我们先改一下代码让这个钩子先返回null。再看,控制台就没有错误警告了。

理解

现在我们也让控制台中不在有错误提醒了,那么我们来探讨第二个问题:snapshot值是什么,这个钩子的字面意思就是:在更新前获取snapshot。那么什么是snapshot?这个单词是快照的意思。大家在阿里云买过云服务器的可能知道,这个快照是把我们的服务器当时的一个运行状态记录下来,当我们的服务器崩溃了,或者出现了一个致命的无法修复的问题的时候,我们可以通过快照来快速将服务器恢复到创建快照的那个时间点的运行状态,有点类似时间倒流的一个操作。

那么我们组件这边更新前获取快照是不是也是这个意思呢?而且我应该将什么值作为快照值来返回呢?其实,任何值都可以是快照值,数字,字符串,对象,都行。我们来试一下:

class Count extends React.Component {
  ...
  getSnapshotBeforeUpdate() {
    console.log("Count--getSnapshotBeforeUpdate");
    // return 123;
    // return {count: 123};
    return "123";
  }
  ...
}

image-20220104153149979

我上面这三种情况我都不会再报错了。

snapshot的去向

通过前面的例子可以看出,其实这个钩子,我返回什么在语法规则以及react中的限制条件中都没有任何错误。但是我这个钩子到底应该返回什么才是合适的呢?而且我们返回的这个快照去哪了呢?用来干什么用呢?

我们来猜一下,getSnapshotBeforeUpdate钩子返回的快照去哪了?按照流程走下去的话就要调用componentDidUpdate钩子了,那么有没有可能是传到这个钩子里了呢?

我们之前一直也没有说过这个钩子有没有参数,我们看一下官方文档:

image-20220104154027870

什么意思?有三个参数对不对?而且第三个参数还就是snapshot。但是目前的情况下我们代码里虽然说写了componentDidUpdate钩子,但是我们接参数了吗?没接。那么我们如果在这里接了snapshot的话,getSnapshotBeforeUpdate钩子的返回值是不是就到了componentDidUpdate钩子里了?

那么我这里有另外一个问题。componentDidUpdate钩子的前两个参数是什么?我们现在代码案例中来看一下:

class Count extends React.Component {
  ...
  componentDidUpdate(prevProps, prevState) {
    console.log("Count--componentDidUpdate",prevProps, prevState);
  }
  ...
}

我们把其他代码先省略了,但是我要说一点,我在源码中是把getSnapshotBeforeUpdate钩子还有getDerivedStateFromProps钩子都注释掉的。我们来看一下这两个参数都传入了什么:

image-20220104154729707

从我们的输出结果来看,第一个是一个空对象,第二个是更新前的state。那么我们类比一下,prevProps参数应该就是更新前的props,但是我们渲染的时候一直也没有传props,所以说这里输出的一直都是空对象。

那么这个时候我们把之前那两个钩子取消注释的话会不会对功能有影响呢?不会有任何影响。那么还是看官方文档。

image-20220104160631383

官方说,如果组件实现了getSnapshotBeforeUpdate钩子那么返回值就会传给componentDidUpdate钩子,那么我们在componentDidUpdate钩子中接一下看看能不能接到

class Count extends React.Component {
  ...
  componentDidUpdate(prevProps, prevState, snapshot) {
    console.log("Count--componentDidUpdate", prevProps, prevState, snapshot);
  }
  ...
}

image-20220104160926822

从图中可以看得出来我们成功在componentDidUpdate钩子中接到了这个快照值,而且确实这个快照值可以为任何值。

我们仙子啊已经是了解了getSnapshotBeforeUpdate钩子的一下基本情况了,那么这个钩子的应用场景什么呢?我们在下节课来通过案例再做进一步的介绍

总结

  • getSnapshotBeforeUpdate钩子在rendercomponentDidUpdate
  • getSnapshotBeforeUpdate钩子必须要有返回值,可以为不是undefined的任何值
  • getSnapshotBeforeUpdate钩子的返回值会顺流传给componentDidUpdate
  • componentDidUpdate会接收组件更新前的snapshotpropsstate

Copyright statement:The articles of this site are all original if there is no special explanation, indicate the source please when you reprint.

Link of this article:https://work.lynchow.com/article/about_get_snapshot_before_update/