企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
[TOC] # batchUpdate * 在 react 的 event handler 内部同步的多次 setState 会被 batch 为一次更新 * 在一个异步的事件循环里面多次 setState,react 不会 batch * 可以使用**ReactDOM.unstable\_batchedUpdates**来强制 batch ## 解释 [https://github.com/facebook/react/issues/10231](https://link.zhihu.com/?target=https%3A//github.com/facebook/react/issues/10231)这里有 Dan Abramov 对此的回答,大致来说就是虽然目前是这样,但是未来 React 希望做到不管里你在哪里写 setState,一个 tick 内的多次 setState 都给你合并掉。 ### 为什么 react 要这么设计? [https://overreacted.io/react-as\-a-ui-runtime/#batching](https://link.zhihu.com/?target=https%3A//overreacted.io/react-as-a-ui-runtime/%23batching)这里有非常好的解释,大致翻译一下 ~~~text function Parent() { let [count, setCount] = useState(0); return ( <div onClick={() => setCount(count + 1)}> Parent clicked {count} times <Child /> </div> ); } function Child() { let [count, setCount] = useState(0); return ( <button onClick={() => setCount(count + 1)}> Child clicked {count} times </button> ); } ~~~ 上面这样的 demo,由于点击事件冒泡的缘故,我们假设如果 react 不 batch 立即更新的话,那么点了 child button 之后的逻辑会是如下这样 ~~~text *** 进入 react click 的事件函数 *** Child (onClick) 触发点击 - setState 修改 state - re-render Child 重新渲染 // 不必要的 Parent (onClick) 触发点击(冒泡) - setState 修改 state - re-render Parent 重新渲染 - re-render Child 重新渲染 (渲染是自顶向下的,父亲更新会导致儿子更新) *** 退出 react click 的事件函数 *** ~~~ 从上面可以看出,第一次子组件的重新渲染完全是浪费的。 所以 React 设计成 setState 不立即触发重新渲染,而是先执行完所有的 event handler,然后用一次重新渲染完成所有更新。 ### 为什么 setTimeout 定时器内的任务没法被 batch 呢? 可以先大致看一下**ReactDOM.unstable\_batchedUpdates**的代码,可以看到有**isBatchingUpdates: boolean**这样一个 flag * [https://github.com/facebook/react/blob/master/packages/react-dom/src/client/ReactDOM.js#L801](https://link.zhihu.com/?target=https%3A//github.com/facebook/react/blob/master/packages/react-dom/src/client/ReactDOM.js%23L801) * [https://github.com/facebook/react/blob/master/packages/react-reconciler/src/ReactFiberScheduler.js#L2481](https://link.zhihu.com/?target=https%3A//github.com/facebook/react/blob/master/packages/react-reconciler/src/ReactFiberScheduler.js%23L2481) 简单来解释,React 的更新是基于[Transaction](https://zhuanlan.zhihu.com/p/28532725)(事务)的,Transacation 就是给目标执行的函数包裹一下,加上前置和后置的 hook (有点类似 koa 的 middleware),在开始执行之前先执行 initialize hook,结束之后再执行 close hook,这样搭配上 isBatchingUpdates 这样的布尔标志位就可以实现一整个函数调用栈内的多次 setState 全部入 pending 队列,结束后统一 apply 了。 但是 setTimeout 这样的方法执行是脱离了事务的,react 管控不到,所以就没法 batch 了。 ### 为什么 Vue 没有这个限制呢? 是因为 vue 采用了 nexttick 的方式,利用 EventLoop,将一个同步事件循环过程中所有修改合并,它本质上属于延迟的批量更新 * [https://github.com/vuejs/vue/blob/dev/src/core/util/next-tick.js](https://link.zhihu.com/?target=https%3A//github.com/vuejs/vue/blob/dev/src/core/util/next-tick.js) * [https://cn.vuejs.org/v2/guide/reactivity.html#%E5%BC%82%E6%AD%A5%E6%9B%B4%E6%96%B0%E9%98%9F%E5%88%97](https://link.zhihu.com/?target=https%3A//cn.vuejs.org/v2/guide/reactivity.html%23%25E5%25BC%2582%25E6%25AD%25A5%25E6%259B%25B4%25E6%2596%25B0%25E9%2598%259F%25E5%2588%2597) ## batchedUpdates 的应用场景 react-easy-state 就使用了 ReactDOM.unstable\_batchedUpdates 来实现了框架层面的 batch 更新 API:[https://github.com/solkimicreb/](https://link.zhihu.com/?target=https%3A//github.com/solkimicreb/react-easy-state/blob/master/src/scheduler.js%23L9) # 参考资料 [深入 react 细节之 - batchUpdate](https://zhuanlan.zhihu.com/p/78516581)