上节课我们讨论了一下生命周期中组件更新时的流程,但是当时流程图中那一块总共有 3 个入口,目前我们才只说了第一个我们最熟悉的也是最简单的。这节课呢我们就来讨论介绍一下稍微要陌生一点的forceUpdate

回顾

在进入这节课的课程之前呢我们先来对上节课的内容来做一个简单的回顾。

  • 组件完成挂载并执行完了componentDidMount之后按顺序执行了 4 个生命周期钩子
  • 通过shouldComponentUpdate方法判断是否允许更新
  • 通过componentWillUpdate方法做更新前的准备工作
  • 通过render方法更新组件
  • 更新组件之后立即调用componentDidUpdate方法
  • shouldComponentUpdate必须返回bool类型
  • 如果没有写shouldComponentUpdate方法,react会自动补齐该方法,并且默认该方法返回true

以上便是我们上节课的主要知识点,那么接下来我们开始我们这节课的学习。

forceUpdate方法

我们上节课可以看得出来,我如果想要走setState这一个流程那么是不是就一定要在组件的某个地方调用setState方法,那么同理,我们要是想要走forceUpdate流程的话也一样要在组件中调用forceUpdate方法。

但是呢,大家是不是觉得我们之前都没用见过这个forceUpdate方法,这个方法到底是个啥啊?我这个方法名是不是可以拆成两个英文单词来看?forceupdate?大家有人要说,这个update我们都知道,就是更新的意思。可是这个force是啥啊?

force这个词呢其实有强制的意思,那么连到一起是什么?forceUpdate就是强制更新。什么叫强制更新呢?我们之前那个setState流程叫正常更新,我们从最一开始学state的时候就说了嘛,我们react的设计哲学就是通过改变state来驱动页面更新。那么这种方法无非就是react中组件更新的一个标准方法,那么这肯定就是正常更新啊。

那么正常更新是什么流程?先更改state然后去询问是否允许组件更新,如果允许,再更新,否则就不更新。那么强制更新呢?肯定就是根本不会去问你允不允许更新,我不管你允不允许,我就要更新。字面上是不是就可以这么理解?

但是我们在回过头来想,我们正常更新的前提是什么?是不是我通过setState修改了state的值,然后触发了这一整套流程?但是如果有些时候我不想去修改state,但是我就是要更新页面,那么我们怎么办?所以react就提供了forceUpdtae方法。这就是forceUpdate方法和setState方法的本质上的区别。

强制更新流程

流程图

执行流程

我们从流程图上面来看的话,是不是觉得也不难啊?强制更新也就是比正常更新少了一个调用shouldComponentUpdate方法的环节。

这个当然也好理解,上节课我们说了,shouldComponentUpdate方法是个什么啊?是不是一个阀门?阀门开启才能往下走到更新流程,但是我们现在要用强制更新了,那么这么可能还会受这个阀门的影响呢?

但是forceUpdate到底都是在什么应用场景下才会用到呢?其实没有很多应用场景的,只有当我们不想更改state的时候但是又想让组件更新的时候才会用。那么我们来测试一下这个流程呗,这个流程我们就不再赘述了啊,就是直接调用forceUpdate触发强制更新流程,然后调用componentWillUpdate方法来做准备工作,紧接着通过render方法来更新组件,待到组件更新完毕立即调用componentDidUpdate方法。

class Count extends React.Component {
  state = { count: 0 };

  componentWillUpdate() {console.log("Count-componentWillUpdate");}
  componentDidUpdate() {console.log("Count-componentDidUpdate");}

  render() {
    console.log("Count-render");
    const { count } = this.state;
    return (
      <div>
        <h2>当前点击 {count} 次</h2>
        <button onClick={this.add}>Add</button>
        <button onClick={this.unmount}>Unmount</button>
        <button onClick={this.force}>Force</button>
      </div>
    );
  }

  add = () => { this.setState({ count: this.state.count + 1 }); };
  unmount = () => {
    ReactDOM.unmountComponentAtNode(document.getElementById("test"));
  };
  force = () => { 
    this.forceUpdate();
  }
}

我们来看一下代码,我们依然还是之前的案例,但是因为我们是强制更新,那么shouldComponentUpdate方法就不需要了,然后我们添加了一个新的按钮叫Force,那么我们说了之前要是想要触发正常更新的流程是不是要调用setState?那么强制更新也一样,我们得调用forceUpdate方法来触发这个流程。那么我们就给Force按钮添加了onClick回调,并在这个回调里面来调用forceUpdate

那么我们怎么知道这个forceUpdate到底有没有被调用呢?如果我们调用了的话是不是就一定会调用componentWillUpdate方法,render方法以及componentDidUpdate方法啊?那么我们来看一下效果:

image-20211230135520720

控制台中打印出了相关方法的调用情况,那么我们再看一下state

image-20211230135637227

我们看见state依然还是 0 ,没有发生任何变化。但页面上好像并没有变化啊,因为我们只是调了一下forceUpdate并没有来做任何的更新操作。

当然又有人要说了,我们没有写shouldComponentUpdate方法,那么到底有没有调用这个方法,那我们再把shouldComponentUpdate方法加上测试一下呗:

class Count extends React.Component {
  state = { count: 0 };

  componentWillUpdate() {...}
  componentDidUpdate() {...}
  shouldComponentUpdate() {
    console.log("Count-shouldComponentUpdate");
    return false;
  }

  render() {...}

  add = () => {...};
  unmount = () => {...};
  force = () => {...}
}

我们让其他代码保持不变,让shouldComponentUpdate方法返回false,看看forceUpdate到底会不会受到shouldComponentUpdate的限制,那我们来看一下效果:

image-20211230140417283

控制台中已经正常按照顺序打印出了整个强制更新流程的钩子调用情况,但是并没有调用shouldComponentUpdate,可见我们确实是已经成功触发了forceUpdate流程。

当我们学完了setStateforceUpdate这两个流程之后有没有觉得react设计得很巧妙?你修改state我可以帮你触发更新流程,或者你只想修改state不想更新组件,那么我们也可以把阀门关上,不更新组件,或者你有特殊需求,只想更新组件不想改state那么我们用forceUpdate方法也可以完成。这样就使得整个组件的灵活性就大大提高了。

总结

  • forceUpdatesetState少一个shouldComponentUpdate环节
  • forceUpdatesetState的本质区别就是forceUpdate不对state做任何更改
  • forceUpdate不受shouldComponentUpdate方法的限制

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/lifecycle_forceupdate_old/