やるぶろ

なんでもやる。垂れ流す。汚いです。

Reduxのセレクタ理解する

お題はTodoアプリ。

idとtitleを持つTaskの配列をstateで管理し、
指定したidのTaskのtitleをセレクタで取得するような処理を書きたい。

まずはざっくり画面を作る。

componentの構成はこんな感じ
Index
└App
 ┝Header
 └TaskList
  └TaskItem

一旦ルーティングやサーバー側の実装はなし。
stateの初期値と変更後の値でごちゃごちゃしてみよう。

TaskSlice.tsxはいろいろ省略してるけどこんな感じ!

export interface Todo {
    id: number
    title: string
    finished: boolean
}

export interface Task {
    id: number
    title: string
    todos: Todo[]
}

export const taskSlice = createSlice({
    name: 'tasks',
    initialState: [
        {
            id: 1,
            title: "task#1",
            todos: [
                {
                    id: 1,
                    title: "todo#1-1",
                    finished: true
                },
                {
                    id: 2,
                    title: "todo#1-2",
                    finished: false
                }
            ]
        },
        {
            id: 2,
            title: "task#2",
            todos: [
                {
                    id: 3,
                    title: "todo#2-1",
                    finished: false
                },
                {
                    id: 4,
                    title: "todo#2-2",
                    finished: false
                }
            ]
        }
    ] as Task[],
    reducers: {},
    extraReducers: {}
})

export default taskSlice

動作確認のためRedux DevToolsを入れる

初期値がStateに入ってる! f:id:momoshima:20211014074115p:plain

ここから、Taskのidを指定するとidが一致するTaskを返すようなメソッドを作りたい
これみながらやる

redux.js.org

セレクター関数には2種類ある * Sliceファイル内に関数として定義 * コンポーネントファイル内で使うuseSelector()

const data = useSelector(state => state.some.deeply.nested.field)

useSelectorを使って上記のような書き方もできるけど設計的にはよろしくない
コンポーネントに同じような記述があると、stateの構造を変更した場合の影響が大きいから レデューサー関数とセレクタのみがstateの構造を知っている必要がある(メモ化というらしい)

メモ化するためにRedux ToolkitパッケージのcreateSelector()を使う

使い方はこんな感じ(公式より)

const selectA = state => state.a
const selectB = state => state.b
const selectC = state => state.c

const selectABC = createSelector([selectA, selectB, selectC], (a, b, c) => {
  // do something with a, b, and c, and return a result
  return a + b + c
})

// Call the selector function and get a result
const abc = selectABC(state)

// could also be written as separate arguments, and works exactly the same
const selectABC2 = createSelector(selectA, selectB, selectC, (a, b, c) => {
  // do something with a, b, and c, and return a result
  return a + b + c
})

自分が使いたいのはこんな感じ?

const selectItems = state => state.items
const selectItemId = (state, itemId) => itemId

const selectItemById = createSelector(
  [selectItems, selectItemId],
  (items, itemId) => items[itemId]
)

selectItemsselectItemIdが入力セレクタ
selectItemByIdが出力セレクタかな?