14.React学习笔记.setState异步/同步分析

一. 为什么使用setState##

  • 直接更改this.state.counter不会引起界面刷新, 这是因为React不知道state发生了改变。
  • React必须通过setState告诉React数据发生了改变。
  • App继承了Component中的setState方法。
    回溯源码
  • Component类中,原型上设置了setState方法。

二. setState异步更新##

  changeText () {
    // 2. setState是异步更新
    this.setState({
      message: "你好啊,wwq",
    }) 
    console.log(this.state.message); // Hello World
  }
  • 上述打印结果为Hello,World,为未改变state前message的值。
  • 这说明setState为异步更新,其还未执行的时候,已经在执行打印了,并不能在执行完setState之后立马拿到最新的state的结果。

三. 重点:setState异步原因##

  • 异步可以显著的提升性能:如果每次调用setState都进行一次更新,意味着render函数会被频繁调用,界面重新渲染,这样效率是很低的。最好的办法是获取到多个更新,之后进行批量更新;
  • 如果同步更新了state,但是还没有执行render函数,那么state和props不能保持同步,若不一致,则会产生很多问题。

四. 获取setState异步更新的数据##

  1. 给setState方法传递一个回调函数。
  2. 使用componentDidUpdate生命周期函数。
  • 方法二调用要先于setState的回调函数。这个与源代码顺序有关。

五. setState同步更新的案例##

  1. 情况一:将setState放入定时器setTimeout中。
  changeText() {
    // 情况一:将setState放入定时器中
    setTimeout(() => {
      this.setState({
        message: "你好啊,wec"
      })
      console.log(this.state.message);
    }, 0)
  }
  1. 情况二:用原生DOM事件绑定来监听click事件
  componentDidMount(){
document.getElementById("btn").addEventListener("click", ()=>{
      this.setState({
        message: "你好啊,cpn"
      })
      console.log(this.state.message);
    })
  }

注意:这分成两种情况##

  1. 在组件生命周期或React合成事件中,setState是异步。如React的onClick是合成事件,和原生DOM的事件监听不一样。
  2. 在setTimeout或者原生DOM事件中,setState是同步。
  • 两者上下文不同,在classComponentUpdater源码中会返回不同的时间。

注意:决定setState异步同步的条件##

回溯源码。

  • 调用Component原型上setState时,执行如下代码:
  this.updater.enqueueSetState(this, partialState, callback, 'setState');
  • 在构造器中会传入updater对象,若没有传入,则默认为ReactNoopUpdateQueue(打印东西,无意义)。
  • 真实开发中,每个组件都会有一个updater对象。

现在追溯到ReactBaseClasses源代码中。

  • 能找到一个classComponentUpdater对象。其对应我们class中的updater。

  • 它把setState放到enqueueSetState队列中。

  • 该队列在之前Component源码中被调用,参数。

  • 其获取了一个Fiber对象。


    image.png

    image.png
  • 之后在computeExpirationForFiber方法中,将currentTime,Fiber还有另一个参数传入。

  • 传入之后在其内部获取fiber.mode,根据当前执行上下文的不同情况来看方法的返回值。

  • 根据不同的优先级,返回Sync或者Batchded。

  • 再之后computeExpirationForFiber()的执行结果被传入createUpdate方法中。该方法记录了更新的方式,然后将该update对象传入enqueueUpdate中(实际为链表)等待处理。

  • 然后调用scheduleWork(fiber, expirationTime),开始更新。

五.

你可能感兴趣的:(14.React学习笔记.setState异步/同步分析)