createSelector
總覽
createSelector
工具程式來自 Reselect 函式庫,重新匯出以方便使用。
有關使用 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 Proxy 包裝的草稿值傳遞到選擇器中,選擇器可能會看到相同的參考並認為沒有任何變更。
然而,一些使用者已要求建立可以在 Immer 驅動的 reducer 內正確運作的選擇器。這樣的一個用例可能是使用 createEntityAdapter
時收集一組已排序的項目,例如 const orderedTodos = todosSelectors.selectAll(todosState)
,然後在 reducer 邏輯的其餘部分使用 orderedTodos
。
除了重新導出 createSelector
之外,RTK 還導出 createSelector
的包裝版本,稱為 createDraftSafeSelector
,它允許你建立可以在 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),
)