createSelector
概述
来自 Reselect 库 的 createSelector
工具,为了方便使用而重新导出。
有关使用 createSelector
的更多详细信息,请参阅
- Reselect API 文档
- React-Redux 文档:Hooks API - 使用记忆选择器
- 惯用 Redux:使用 Reselect 选择器进行封装和性能优化
- React/Redux 链接:Reducer 和选择器
在 v0.7 之前,RTK 从 selectorator
重新导出了 createSelector
,这允许使用字符串键路径作为输入选择器。这已被移除,因为它最终没有提供足够的益处,并且字符串键路径使得选择器的静态类型变得困难。
createDraftSafeSelector
一般来说,我们建议不要在 reducer 内部使用选择器。
- 选择器通常期望整个 Redux 状态对象作为参数,而切片 reducer 只能访问整个 Redux 状态的特定子集。
- Reselect 的
createSelector
依赖于引用比较来确定输入是否已更改,如果将 Immer 代理包装的草稿值传递给选择器,选择器可能会看到相同的引用并认为没有发生更改。
但是,一些用户要求能够创建在 Immer 支持的 reducer 内部正常工作的选择器。此功能的一个用例可能是使用 createEntityAdapter
收集有序的项目集,例如 const orderedTodos = todosSelectors.selectAll(todosState)
,然后在 reducer 逻辑的其余部分使用 orderedTodos
。
除了重新导出 createSelector
之外,RTK 还导出一个名为 createDraftSafeSelector
的 createSelector
的包装版本,它允许您创建可以在 createReducer
和 createSlice
reducer 内部安全使用的选择器,这些 reducer 使用 Immer 支持的可变逻辑。当与普通状态值一起使用时,选择器仍将根据输入正常记忆。但是,当与 Immer 草稿值一起使用时,选择器将偏向于重新计算结果,以确保安全。
默认情况下,由 entityAdapter.getSelectors
创建的所有选择器都是“草稿安全”选择器。
示例
const selectSelf = (state: State) => state
const unsafeSelector = createSelector(selectSelf, (state) => state.value)
const draftSafeSelector = createDraftSafeSelector(
selectSelf,
(state) => state.value,
)
// in your reducer:
state.value = 1
const unsafe1 = unsafeSelector(state)
const safe1 = draftSafeSelector(state)
state.value = 2
const unsafe2 = unsafeSelector(state)
const safe2 = draftSafeSelector(state)
执行完这些操作后,unsafe1
和 unsafe2
将具有相同的值,因为记忆的选择器是在同一个对象上执行的 - 但是 safe2
实际上将与 safe1
不同(具有更新的值 2
),因为安全选择器检测到它是在 Immer 草稿对象上执行的,并使用当前值重新计算,而不是返回缓存的值。
createDraftSafeSelectorCreator
RTK 还导出一个 createDraftSafeSelectorCreator
函数,它是 createSelectorCreator
的“草稿安全”等效项。
import {
createDraftSafeSelectorCreator,
weakMapMemoize,
} from '@reduxjs/toolkit'
const createWeakMapDraftSafeSelector =
createDraftSafeSelectorCreator(weakMapMemoize)
const selectSelf = (state: State) => state
const draftSafeSelector = createWeakMapDraftSafeSelector(
selectSelf,
(state) => state.value,
)
定义预类型化的 createDraftSelector
从 RTK 2.1 开始,您可以定义 createDraftSafeSelector
的“预类型化”版本,该版本可以内置 state
的类型。这使您能够一次性设置这些类型,因此您不必每次调用 createDraftSafeSelector
时都重复它们。
const createTypedDraftSafeSelector =
createDraftSafeSelector.withTypes<RootState>()
导入并使用预类型化的 createTypedDraftSafeSelector
函数,它将自动知道 state
参数的类型为 RootState
。
目前,这种方法仅在输入选择器作为单个数组提供时有效。
如果您将输入选择器作为单独的内联参数传递,则结果函数的参数类型将不会被推断。作为解决方法,您可以:
- 将您的输入选择器包装在一个数组中
- 您可以注释结果函数的参数类型
import { createSelector } from 'reselect'
interface Todo {
id: number
completed: boolean
}
interface Alert {
id: number
read: boolean
}
export interface RootState {
todos: Todo[]
alerts: Alert[]
}
export const createTypedDraftSafeSelector =
createDraftSafeSelector.withTypes<RootState>()
const selectTodoIds = createTypedDraftSafeSelector(
// Type of `state` is set to `RootState`, no need to manually set the type
(state) => state.todos,
// ❌ Known limitation: Parameter types are not inferred in this scenario
// so you will have to manually annotate them.
(todos: Todo[]) => todos.map(({ id }) => id),
)