跳至主要内容

createSelector

概述

来自 Reselect 库createSelector 工具,为了方便使用而重新导出。

有关使用 createSelector 的更多详细信息,请参阅

注意

在 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 还导出一个名为 createDraftSafeSelectorcreateSelector 的包装版本,它允许您创建可以在 createReducercreateSlice 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)

执行完这些操作后,unsafe1unsafe2 将具有相同的值,因为记忆的选择器是在同一个对象上执行的 - 但是 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

已知限制

目前,这种方法仅在输入选择器作为单个数组提供时有效。

如果您将输入选择器作为单独的内联参数传递,则结果函数的参数类型将不会被推断。作为解决方法,您可以:

  1. 将您的输入选择器包装在一个数组中
  2. 您可以注释结果函数的参数类型
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),
)