メインコンテンツへスキップ

createSelector

概要

ReselectライブラリcreateSelectorユーティリティを、使いやすくするために再エクスポートしています。

createSelectorの使い方に関する詳細は、以下を参照してください。

注意

v0.7より前では、RTKはselectoratorからcreateSelectorを再エクスポートしており、文字列キーパスを入力セレクターとして使用できました。これは最終的に十分なメリットを提供せず、文字列キーパスがセレクターの静的型付けを困難にしたため、削除されました。

createDraftSafeSelector

一般的に、リデューサー内でセレクターを使用することはお勧めしません。

  • セレクターは通常、Redux状態オブジェクト全体を引数として期待しますが、スライスリデューサーはRedux状態全体の特定の部分にのみアクセスできます。
  • ReselectのcreateSelectorは、入力が変更されたかどうかを判断するために参照比較に依存しており、Immer Proxyでラップされたドラフト値がセレクターに渡された場合、セレクターは同じ参照を見て、何も変更されていないと考える可能性があります。

ただし、一部のユーザーは、Immerで駆動されるリデューサー内で正しく動作するセレクターを作成する機能を要求しています。このユースケースの1つは、createEntityAdapterを使用する際に、const orderedTodos = todosSelectors.selectAll(todosState)のように、順序付けられたアイテムのセットを収集し、残りのリデューサーロジックでorderedTodosを使用することなどが考えられます。

createSelectorの再エクスポートに加えて、RTKはcreateDraftSafeSelectorという名前のcreateSelectorのラップされたバージョンもエクスポートしています。これにより、Immerで駆動される可変ロジックを持つcreateReducerおよびcreateSliceリデューサー内で安全に使用できるセレクターを作成できます。プレーンな状態値で使用する場合、セレクターは入力に基づいて通常どおりメモ化されます。ただし、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は同じ値になります。しかし、安全なセレクターはImmerドラフトオブジェクトで実行されたことを検出し、キャッシュされた値を返す代わりに現在の値を使用して再計算したため、safe2は実際にはsafe1とは異なります(2の更新された値)。

createDraftSafeSelectorCreator

RTKは、createSelectorCreatorの「ドラフトセーフ」版であるcreateDraftSafeSelectorCreator関数もエクスポートしています。

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以降では、stateの型を組み込むことができる、事前型付けされたcreateDraftSafeSelectorのバージョンを定義できます。これにより、これらの型を一度設定するだけで、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),
)