美文网首页
React batchUpdates - setState 批量

React batchUpdates - setState 批量

作者: 晓风残月1994 | 来源:发表于2020-07-07 00:37 被阅读0次

节选自 React 源码笔记(尚未研究完),由于知识粒度较为合适,所以单独先贴出来。

顾名思义,批量更新,可以避免短期内的多次渲染,攒为一次性更新。

在后面提供的 demo 中的 handleClick 中有三种方式调用 this.countNumber()

  1. 事件处理函数自带batchedUpdates
  2. setTimeout中没有batchedUpdates
  3. 主动batchedUpdates

第1种:

批量更新,会打印 0 0 0,然后按钮文本显示为3。每次 setState 虽然都会经过 enqueueUpdate (创建update 并加入队列)-> scheduleWork(寻找对应的 FiberRoot 节点)-> requestWork (把 FiberRoot 加入到调度队列),可惜上下文变量 isBatchingUpdates 在外部某个地方被标记为了 true ,因此本次 setState 一路走来,尚未到达接下来的 performSyncWork 或者 scheduleCakkbackWithExpirationTime 就开始一路 return 出栈:

image.png

isBatchingUpdates 变量在早前的调用栈中(我们为 onClick 绑定的事件处理函数会被 react 包裹多层),被标记为了 true ,然后 fn(a, b) 内部经过了3次 setState 系列操作,然后 finally 中 isBatchingUpdates 恢复为之前的 false,此时执行同步更新工作 performSyncWork

image.png

第2种:

handleClick 中使用 setTimeoutthis.countNumber 包裹了一层 setTimeout(() => { this.countNumber()}, 0) ,同样要调用 handleClick 也是先经过 interactiveUpdates$1 上下文,也会执行 setTimeout ,然后 fn(a, b) 就执行完了,因为最终是浏览器来调用 setTimeout 的回调 然后执行里面的 this.countNumber ,而对于 interactiveUpdates$1 来说继续把自己的 performSyncWork 执行完,就算结束了。显然不管 performSyncWork 做了什么同步更新,我们的 ​setState 目前为止都还没得到执行。然后等到 ​setTimeout 的回调函数等到空闲被执行的时候,才会执行 ​setState ,此时没有了批量更新之上下文,所以每个 ​setState 都会单独执行一遍 ​requestWork 中的 ​performSyncWork 直到渲染结束,且不会被打断,3次 ​setState 就会整个更新渲染 3 遍(这样性能不好,所以一般不会这样写 react)。

什么叫不会被打断的同步更新渲染?看一下 demo 中的输出,每次都同步打印出了最新的 button dom 的 ​innerText

第3种:

已经可以猜到,无非就是因为使用 ​setTimeout 而“错过了”第一次的批量更新上下文,那等到 ​setTimeout 的回调执行的时候,专门再创建一个批量更新上下文即可:

image.png

总结:

setState 是同步还是异步?

setState 方法本身是被同步调用,但并不代表 react 的 state 就会被立马同步地更新,而是要根据当前执行上下文来判断。

如果处于批量更新的情况下,state 不会立马被更新,而是批量更新。

如果非批量更新的情况下,那么就“有可能”是立马同步更新的。为什么不是“一定”?因为如果 React 开启了 Concurrent Mode,非批量更新会进入之前介绍过的异步调度中(时间分片)。


批量更新演示 demo:

import React from 'react'
import { unstable_batchedUpdates as batchedUpdates } from 'react-dom'

export default class BatchedDemo extends React.Component {
  state = {
    number: 0,
  }

  handleClick = () => {
    // 事件处理函数自带batchedUpdates
    this.countNumber()

    // setTimeout中没有batchedUpdates
    // setTimeout(() => {
    //   this.countNumber()
    // }, 0)

    // 主动batchedUpdates
    // setTimeout(() => {
    //   batchedUpdates(() => this.countNumber())
    // }, 0)
  }

  countNumber() {
    const button = document.getElementById('myButton')
    const num = this.state.number
    this.setState({
      number: num + 1,
    })
    console.log(this.state.number)
    console.log(button.innerText)
    this.setState({
      number: num + 2,
    })
    console.log(this.state.number)
    console.log(button.innerText)
    this.setState({
      number: num + 3,
    })
    console.log(this.state.number)
    console.log(button.innerText)
  }

  render() {
    return <button id="myButton" onClick={this.handleClick}>Num: {this.state.number}</button>
  }
}

相关文章

网友评论

      本文标题:React batchUpdates - setState 批量

      本文链接:https://www.haomeiwen.com/subject/rhanqktx.html