十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

React-Redux 的 batch() API:合并 React 渲染更新、优化多 action 批量分发实战指南

React-Redux 的 batch() API:合并 React 渲染更新、优化多 action 批量分发实战指南 React-Redux 的 batch() API合并 React 渲染更新、优化多 action 批量分发实战指南【免费下载链接】react-reduxOfficial React bindings for Redux项目地址: https://gitcode.com/gh_mirrors/re/react-reduxbatch()是 React-Redux 自 v7.0.0 起公开导出的一个工具函数其作用是把同一事件循环内、位于 React 事件系统之外的多次状态更新合并为一次渲染提交。本文以仓库中 v7.0 版 batch 文档 为主体骨架结合当前仓库的源码实现src/utils/batch.ts、src/exports.ts、src/utils/Subscription.ts与测试用例讲解batch()的由来、用法、内部实现与演进帮助读者理解在 React 18 之前如何避免一次分发多次渲染的性能浪费以及 React 18 之后该 API 为何退化为无操作no-op。一、batch()是什么batch()是 React-Redux 在 v7.0.0 中新增并公开导出的 API函数签名如下batch(fn: Function)它接收一个回调函数并在调用期间把其中产生的所有 React 状态更新收集起来合并为单次渲染提交。官方文档给出的定位非常明确当你在 React 事件处理器之外分发多个 Redux action 时用它来保证多个 action 只引起一次渲染更新。import { batch } from react-redux function myThunk() { return (dispatch, getState) { // should only result in one combined re-render, not two batch(() { dispatch(increment()) dispatch(increment()) }) } }在这个 thunk 示例中batch()回调内的两次dispatch(increment())虽然依次触发了两次 store 更新通知但最终只会导致订阅组件被合并成一次重渲染而不是两次。二、为什么要设计batch()React 批处理机制的背景要理解batch()的价值需要先了解 React 的批处理batching机制。React 的unstable_batchedUpdates()API 允许把同一事件循环 tick 内的所有 React 更新合并到单次渲染提交中。关键事实是React内部已经在自己的事件处理回调中对更新做了批处理但该 API 属于renderer 包如 ReactDOM、React Native而非 React 核心本身。也就是说在 React 18 的自动批处理automatic batching出现之前批处理只发生在 React 自己管理的事件路径上。对于 Redux 这类外部状态源来说常见的非 React 路径包括setTimeout/setInterval回调Promise 的.then()与async/await后续代码原生 DOM 事件监听器WebSocket / SSE 消息回调Redux-thunk、Redux-Saga 等中间件中异步触发的连续分发。在这些路径里连续dispatch多个 actionReact 18 之前的 ReactDOM 会为每次 store 更新触发一次同步渲染导致多余的重渲染开销。这正是 React-Redux 暴露batch()的直接原因。三、React-Redux 如何把batch()提供给用户由于 React-Redux 需要同时运行在 ReactDOM 和 React Native 两种环境下而批处理 API 又位于各自的 renderer 包中React-Redux 在构建时负责从正确的 renderer 导入该 API 供内部使用并把它重命名为batch()对外公开。这一点可以从仓库的 API 报告文件 etc/react-redux.api.md 中得到佐证其顶部对batch的来源声明为import { unstable_batchedUpdates as batch } from react-dom也就是说在 v7 时代batch本质上就是 ReactDOM 的unstable_batchedUpdates的再导出——它在构建期被固定绑定到正确的 renderer 实现从而屏蔽了 ReactDOM 与 React Native 之间的差异。同时batch与Provider、connect、useSelector等一起被列在包的主入口导出清单中见 src/exports.tsconst batch defaultNoopBatch export { Provider, batch, connect, legacy_connect, shallowEqual }四、源码视角batch()在 React-Redux 内部的真实用法batch()不只是给用户用的工具它也是 React-Redux 订阅通知机制的核心环节。在 src/utils/Subscription.ts 中订阅模块把defaultNoopBatch以batch的别名导入并用它包裹整条监听器通知链import { defaultNoopBatch as batch } from ./batch // ... notify() { batch(() { let listener first while (listener) { listener.callback() listener listener.next } }) },这段代码的含义是当 store 状态变化时Subscription会遍历所有已订阅的监听器对应各个connect组件或 hooks 消费者而整轮遍历被放在batch()回调内执行。这样同一轮 store 更新触发的所有组件通知会在一个批处理单元内完成从而保证祖先组件先于后代组件重渲染订阅模块注释中明确写明了这一自上而下的更新保证并让多个被通知组件共享一次渲染提交。测试代码也印证了这一内部行为。在 test/components/Provider.spec.tsx 中测试注释直接写道 Provider uses unstable_batchedUpdates() under the hoodProvider 底层使用了unstable_batchedUpdates()并在rtl.act()中连续分发 action 后断言子组件只被调用一次。此外test/components/connect.spec.tsx 中也有 setState calls DOM handlers are batchedDOM 事件处理器中的 setState 调用会被批处理的断言。五、演进从unstable_batchedUpdates到 no-op值得特别说明的是当前仓库v9.x中的batch已经是一个立即执行回调的 no-op 实现其定义见 src/utils/batch.ts// Default to a dummy batch implementation that just runs the callback export function defaultNoopBatch(callback: () void) { callback() }src/exports.ts中也对batch标注了明确的弃用说明见 src/exports.ts/** * deprecated As of React 18, batching is enabled by default for ReactDOM and React Native. * This is now a no-op that immediately runs the callback. */ const batch defaultNoopBatch这一演进对应 React 18 的自动批处理automatic batching特性React 18 起无论更新在何处排队ReactDOM 与 React Native 都会自动批量处理所有状态更新。因此当前版本官方文档 docs/api/batch.md 明确提示如果你正在使用 React 18你不需要使用batchAPI。React 18 会自动批处理所有状态更新无论它们在何处排队。换句话说batch()的历史使命是弥补 React 18 之前React 事件系统之外不自动批处理的缺口React 18 之后该缺口被框架本身填平batch()保留下来仅仅是为了向后兼容——调用它只会立即执行回调不会产生额外效果。需要说明的是上述自动批处理能力来源于 React 18 的官方设计而 React-Redux 侧以 no-op 承接兼容性这一事实以 src/utils/batch.ts 与 src/exports.ts 的代码为准。六、实用指南什么场景还需要或不需要batch()综合上述演进可以给出如下使用结论React 18含 createRoot之前的环境在 React 事件处理器、生命周期之外连续分发多个 action如 thunk 内的连续 dispatch、定时器回调、异步请求完成后的多次 dispatch建议用batch(() { ... })包裹把多次渲染合并为一次示例即本文第一节的 thunk 代码注释明确写出应该只产生一次合并重渲染而不是两次。React 18 及之后的环境不需要手动调用batch()框架已自动批处理调用batch()也不会出错它只是立即执行回调no-op可作为过渡期代码保持兼容。边界与限制当前仓库为 React Server Components 提供了独立入口 src/index-rsc.ts其中batch与Provider、connect、hooks 等一样被替换为throwNotSupportedError——在 RSC 环境中调用batch会直接抛出错误并提示此导出仅支持在 Client Component 中使用batch()只合并同一回调内排队的更新跨回调如两个独立的setTimeout的更新不会被合并包的依赖与运行环境前提见 package.json 的peerDependenciesreact: ^18.0 || ^19、redux: ^5.0.0并结合 tsup.config.ts 中多目标构建产物理解其在不同模块格式下的分发方式。七、延伸阅读指引原文档 v7.0 版 batch 文档 末尾附有两条参考来源此处以文字形式说明其内容要点不在文章中嵌入外部链接React 官方关于unstable_batchedUpdates()API 的引入提交是理解该 API 设计意图的第一手资料React 18 工作组关于Automatic Batching for Fewer Renders自动批处理、更少渲染的公开讨论编号 #21详细阐述了 React 18 如何把批处理扩展到所有更新场景也是batch()退化为 no-op 的官方背景。仓库内同一主题的更多版本资料还包括 v7.1 版 batch 文档、v7.2 版 batch 文档 以及当前的 docs/api/batch.md内容一致可对照阅读以了解该 API 在版本演进中的措辞变化如 v7.1 文档中的unstable_batchedUpdate()与unstable_batchedUpdates()拼写差异即属历史版本遗留。总结batch()是 React-Redux 为应对 React 18 之前React 事件系统之外不自动批处理这一限制而提供的官方工具它把unstable_batchedUpdates从正确的 renderer 在构建期导入并公开重导出内部用它包裹订阅通知循环以保证批量渲染外部用它帮助用户在 thunk、异步回调等场景合并多次 dispatch 引发的渲染。到了 React 18 的自动批处理时代它按设计退化为立即执行回调的 no-op以最小代价保持 API 兼容。理解这条从必要工具到兼容占位的演进线既能写出更高效的 React-Redux 代码也能准确判断新老版本项目各自的最优实践。【免费下载链接】react-reduxOfficial React bindings for Redux项目地址: https://gitcode.com/gh_mirrors/re/react-redux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表