尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Redux 如何减少连续 dispatch 带来的订阅通知和 React 重渲染次数

Redux 如何减少连续 dispatch 带来的订阅通知和 React 重渲染次数 Redux 如何减少连续 dispatch 带来的订阅通知和 React 重渲染次数【免费下载链接】reduxA JS library for predictable global state management项目地址: https://gitcode.com/gh_mirrors/re/redux当一个 action creator、thunk 或异步回调在同一个逻辑操作里连续 dispatch 多个 action 时每一个 action 都会触发一次订阅通知在 React 环境中往往还伴随一次完整的渲染。Redux 文档中明确了这种场景的成本并给出了几条可落地的削减手段把多个 dispatch 合并为一个 action、用 React-Redux 的batch()把多次渲染合并成一次、或用 store enhancer 对订阅通知做批量处理。这篇文章按“先合并、再批处理”的顺序给出每条路径的具体写法。先确认问题出在哪每次 dispatch 都会通知所有订阅者Redux store 在每个成功派发的 action即 action 到达 store 并被 reducer 处理之后通知订阅者。这一点决定了连续 dispatch 的直接代价style guide 中说明React 事件处理器中排队的 UI 更新通常会被合并进一次 React 渲染但在事件处理器之外大多数async函数、timeout 回调、非 React 代码排队的更新不会合并——每个 dispatch 都会在一次完整的同步 React 渲染路径完成后才结束这会拖慢性能见 style guide 的 Avoid Dispatching Many Actions Sequentially 一节。连续 dispatch 还有正确性问题如果多个 dispatch 在概念上属于同一个“事务”前几个 dispatch 之后会留下中间状态其他应用逻辑可能认为这些状态无效。例如依次派发UPDATE_A、UPDATE_B、UPDATE_C在第二个 dispatch 之后a、b、c中只有一部分被更新状态是“不完整”的。所以在动手做批处理之前先按 FAQ: Actions 的建议检查这些 action 是相互独立的多个事件还是一个事件被拆成了多个 action文档给出的判断原则是“避免为了性能担忧的地点连续同步 dispatch 多次”并在 reducer 可读性和 action 日志可读性之间取得平衡。首选合并为单个 action从源头减少通知次数如果多个 dispatch 本质上是同一次状态更新的不同侧面最直接的做法是只 dispatch 一个“事件”类型的 action让一次派发完成全部对应的状态更新。style guide 原话是Prefer dispatching a single event-type action that results in all of the appropriate state updates at once, or consider use of action batching addons to dispatch multiple actions with only a single UI update at the end.这个方案不需要任何额外依赖改 action creator让它在一次dispatch中携带完成整个更新所需的数据对应的 reducer case 一次性写出所有状态变化。判断标准也很明确——如果合并后 action 日志仍然可读type 有意义、能看出发生了什么就用合并方案。React 应用用 React-Redux 的batch()合并渲染如果多个 dispatch 确实必要多个相互独立的更新而你又使用 React-Redux从 React-Redux v7 起提供了batch公共 API专门用于在React 事件处理器之外dispatch 时最小化 React 重渲染次数见 FAQ: Performance 的 How can I reduce the number of store update events? 一节它包装了 React 渲染层ReactDOM、React Native 等 renderer 包的unstable_batchedUpdate()API把同一个事件循环 tick 内的所有 React 更新合并为一次渲染路径。React-Redux 在构建时会从正确的 renderer 导入该 API并以batch()的名称再导出ReactDOM 和 React Native 环境都可用。文档给出的用法示例在 thunk 中连续 dispatch 两次incrementimport { batch } from react-redux function myThunk() { return (dispatch, getState) { // should only result in one combined re-render, not two batch(() { dispatch(increment()) dispatch(increment()) }) } }注意文档对这个例子的预期表述是“应该只产生一次合并后的重渲染而不是两次”——即判断标准是渲染次数从每次 dispatch 各一次变为一次。存储层用 enhancer 把多次 dispatch 压成一次订阅通知如果要减少的是store 层面的订阅通知次数对所有订阅者生效不限于 ReactRedux 文档列出了几类批处理 addon分别位于 FAQ: Performance 和 Redux Ecosystem 的 Batching 小节库形式工作方式redux-batched-actionshigher-order reducer把多个 action 当作一个派发在 reducer 中“解包”redux-batched-subscribestore enhancer对多次 dispatch 产生的订阅回调做 debounceredux-batchstore enhancer支持一次 dispatch 一个 action 数组只产生一次订阅通知redux-batch-actions-enhancerstore enhancer接受批处理 action 的 enhancerEcosystem 文档给出了各自的接入代码。以redux-batch为例在 store 创建时通过enhancers注入之后直接 dispatch 一个数组const store configureStore({ reducer, enhancers: existingEnhancersArray [ reduxBatch, ...existingEnhancersArray, reduxBatch ] }) store.dispatch([{ type: INCREMENT }, { type: INCREMENT }])用redux-batched-subscribe做 debounce 时则传入一个防抖过的 notify 函数const debounceNotify _.debounce(notify notify()) const store configureStore({ reducer, enhancers: [batchedSubscribe(debounceNotify)] })redux-batched-actions走 reducer 一侧不 dispatch 数组而是用batchActions包装const store configureStore({ reducer: enableBatching(rootReducer) }) store.dispatch(batchActions([{ type: INCREMENT }, { type: INCREMENT }]))使用这些方案时行为保证以 FAQ: Design Decisions 的说明为准Redux 保证最终会用当前可用的最新状态调用所有订阅者但不保证每个 action 都调用一次每个订阅者订阅者内拿状态应始终通过store.getState()而不是依赖 action 参数订阅者拿不到 action正是为了不破坏批处理方式。如果你已经在用 Redux Toolkit 2.x迁移文档说明configureStore现在默认加入autoBatchEnhancer当多个 “low-priority” action 连续派发时它会短暂延迟通知订阅者从而提升性能见 RTK 2 迁移文档。在自定义enhancers回调时用getDefaultEnhancers保留该默认增强器即可例如const store configureStore({ reducer, enhancers: getDefaultEnhancers { return getDefaultEnhancers({ autoBatch: { type: tick } }).concat(myEnhancer) } })如果直接返回自定义 enhancer 数组而丢掉getDefaultEnhancers的结果批处理增强器以及默认 middleware 的 enhancer都会丢失configureStore会在 console 报错提示这种遗漏。验证与边界验证方式按文档给出的预期来对照React 侧用batch()包裹的连续 dispatch按文档示例的表述预期结果是“one combined re-render, not two”——即原来每个 dispatch 各触发一次渲染现在只触发一次。可以在改前后对比同一操作中组件的渲染次数变化。Store 侧使用 batch enhancer 后连续 dispatch如 dispatch 一个 action 数组只产生一次订阅通知订阅者通过store.getState()拿到的始终是最新状态。边界与限制批处理只减少通知/渲染次数不改变 reducer 执行本身每个 action 仍会被 reducer 处理只是订阅通知被合并或延迟。debounce 类方案redux-batched-subscribe意味着通知是“延迟”的订阅者收到的状态是防抖窗口结束时的最新状态而不是每次 dispatch 之后的即时状态。如果连续 dispatch 是为了表达一个“事务”而不只是性能考虑优先选择合并为单个 action 的方案它能同时消除中间无效状态的问题而批处理只解决通知次数问题。相关文档FAQ: Performance — “How can I reduce the number of store update events?” 及相关链接Style Guide — Avoid Dispatching Many Actions SequentiallyRedux Ecosystem — Batching 小节的各库接入示例FAQ: Design Decisions — 订阅通知可批处理的保证与订阅者拿状态的方式FAQ: Actions — “Should I dispatch multiple actions in a row from one action creator?”【免费下载链接】reduxA JS library for predictable global state management项目地址: https://gitcode.com/gh_mirrors/re/redux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表