美文网首页
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