跳到主要内容

 

autoBatchEnhancer

这是一个Redux存储增强器,它会寻找一行或多行被派发的"低优先级"动作,并在延迟上排队一个回调来运行订阅者通知。然后,它会在排队的回调运行时,或者当下一个"正常优先级"的动作被派发时,通知订阅者,以先到者为准。

基本使用

import {
createSlice,
configureStore,
autoBatchEnhancer,
prepareAutoBatched,
} from '@reduxjs/toolkit'

interface CounterState {
value: number
}

const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 } satisfies CounterState as CounterState,
reducers: {
incrementBatched: {
// 批处理,低优先级
reducer(state) {
state.value += 1
},
// 使用 `prepareAutoBatched` 实用程序自动
// 添加增强器需要的 `action.meta[SHOULD_AUTOBATCH]` 字段
prepare: prepareAutoBatched<void>(),
},
// 未批处理,正常优先级
decrementUnbatched(state) {
state.value -= 1
},
},
})
const { incrementBatched, decrementUnbatched } = counterSlice.actions

// 默认包含批处理增强器,从RTK 2.0开始
const store = configureStore({
reducer: counterSlice.reducer,
})

API

autoBatchEnhancer

autoBatchEnhancer 签名
export type SHOULD_AUTOBATCH = string
type AutoBatchOptions =
| { type: 'tick' }
| { type: 'timer'; timeout: number }
| { type: 'raf' }
| { type: 'callback'; queueNotification: (notify: () => void) => void }

export type autoBatchEnhancer = (options?: AutoBatchOptions) => StoreEnhancer
提示

从RTK 2.0开始,调用 configureStore 时默认包含 autoBatchEnhancer

这意味着要配置它,你应该传递一个回调,该回调接收 getDefaultEnhancers 并用你想要的设置调用它。

使用 getDefaultEnhancers 配置 autoBatchEnhancer
import { configureStore } from '@reduxjs/toolkit'

const store = configureStore({
reducer: () => 0,
enhancers: (getDefaultEnhancers) =>
getDefaultEnhancers({
autoBatch: { type: 'tick' },
}),
})

创建一个新的 autobatch 存储增强器实例。

任何带有 action.meta[SHOULD_AUTOBATCH] = true 标签的动作都将被视为"低优先级",并将排队一个通知回调。增强器将延迟通知订阅者,直到以下任一情况发生:

  • 排队的回调运行并触发通知
  • 在同一刻派发一个"正常优先级"的动作(任何没有 action.meta[SHOULD_AUTOBATCH] = true 的动作)

autoBatchEnhancer 接受选项来配置如何排队通知回调:

  • {type: 'raf'}:使用 requestAnimationFrame 排队(默认)
  • {type: 'tick'}:使用 queueMicrotask 排队
  • {type: 'timer', timeout: number}:使用 setTimeout 排队
  • {type: 'callback', queueNotification: (notify: () => void) => void}:让你提供自己的回调,例如一个防抖或节流函数

默认行为是使用 requestAnimationFrame 排队通知。

SHOULD_AUTOBATCH 值应该是不透明的 - 它目前是一个字符串,为了简单起见,但在未来可能是一个 Symbol

prepareAutoBatched

prepareAutoBatched 签名
type prepareAutoBatched = <T>() => (payload: T) => { payload: T; meta: unknown }

创建一个函数,该函数接受一个 payload 值,并返回一个带有 {payload, meta: {[SHOULD_AUTOBATCH]: true}} 的对象。这是为了与RTK的 createSlice 及其 "prepare 回调"语法一起使用:

createSlice({
name: 'todos',
initialState,
reducers: {
todoAdded: {
reducer(state, action: PayloadAction<Todo>) {
state.push(action.payload)
},
prepare: prepareAutoBatched<Todo>(),
},
},
})

批处理方法和背景

文章 A Comparison of Redux Batching Techniques 描述了四种不同的"批处理 Redux 动作/派发"的方法:

  • 一个高阶 reducer,它接受一个真实动作中嵌套的多个动作,并一起迭代它们
  • 一个包装 dispatch 并防抖通知回调的增强器
  • 一个包装 dispatch 以接受动作数组的增强器
  • React 的 unstable_batchedUpdates(),它只是将多个排队的渲染合并为一个,但不影响订阅者通知

这个增强器是"防抖"方法的一个变体,但有一个转折。

它不仅仅是防抖所有订阅者通知,而是观察任何带有特定 action.meta[SHOULD_AUTOBATCH]: true 字段的动作。

当它看到带有该字段的动作时,它会排队一个回调。reducer 立即更新,但增强器不会立即通知订阅者。如果在连续的动作中派发了带有相同字段的其他动作,增强器将继续不通知订阅者。然后,当排队的回调运行时,它最终会通知所有订阅者,类似于 React 如何批处理重新渲染。

额外的转折也受到了 React 将更新分为"低优先级"和"立即"行为(如由 AJAX 请求排队的渲染 vs 应同步处理的用户输入排队的渲染)的启发。

如果已经派发了一些低优先级的动作并排队了一个通知微任务,然后派发了一个正常优先级的动作(没有该字段),增强器将像往常一样同步通知所有订阅者,并在刻度结束时不通知他们。

这允许 Redux 用户选择性地为某些动作标记以实现有效的批处理行为,使这完全是每个动作的选择,同时保留所有其他动作的正常通知行为。

RTK 查询和批处理

RTK Query 已经将其几个关键的内部动作类型标记为可批处理。通过将 autoBatchEnhancer 添加到存储设置中,它提高了整体的 UI 性能,特别是在渲染使用 RTKQ 查询钩子的大型组件列表时。