ReduxToolsのreducersとextraReducersってどう違うんだ?

ReduxToolsのreducersとextraReducersってどう違うんだ?

ReduxToolkitの基本的なことは公式を見てください。

extraReducersはスライスをまたいでアクションを受け取ることが出来る。

試したこと。

5つのソース

犬猿の仲ではないですが、猿と犬のスライスを作って実験しました。
スライスは./slices/monkeySlice.ts./slices/dogSlice.tsを用意
んでストアのstore.tsと表示用のTestControl.tsxを用意
hooks.tsはお決まりのやつ。

合計5つのファイルを用意。

hooks.ts

お決まりのやつ


import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import { AppDispatch, RootState } from "./store";

export const useAppDispatch: () => AppDispatch = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

slices/monkeySlice.ts

import { createAction, createSlice } from "@reduxjs/toolkit";

export const ukkiAction = createAction('ukki');

// 追加
export const monkeyUkkiAction = createAction('monkey/ukki');

const initialState = { x: 123 }

const monkeySlice = createSlice({
    name: 'monkey',
    initialState,
    reducers: {

        // monkey/ukki
        ukki: (s, x) => {
            console.log(`monkey.reducer:ukki = ${x.payload} / ${x.type}`)
        },

        // monkey/wan
        wan: (s, x) => {
            console.log(`monkey.reducer:wan = ${x.payload} / ${x.type}`)
        }
    },

    extraReducers: (builder) => {

        // ukki
        builder.addCase(
            ukkiAction,
            (s, x) => {
                console.log(`monkey.extra:ukki = ${x.payload} / ${x.type}`);
            }
        )

        // monkey/wan
        builder.addCase(
            monkeySlice.actions.wan,
            (s, x) =>
            {
                console.log(`monkey.extra:wan = ${x.payload} / ${x.type}`);
            }
        )

        // monkey/ukki
        builder.addCase(
            monkeyUkkiAction,
            (s, x) => {
                console.log(`monkey.extra:monkey/ukki = ${x.payload} / ${x.type}`);
            }
        )

    }
})

export default monkeySlice;

console.log(ukkiAction.type); // ukki
console.log(monkeyUkkiAction.type); // monkey/ukki
console.log(monkeySlice.actions.ukki.type); // monkey/ukki
console.log(monkeySlice.actions.wan.type); // monkey/wan

slices/dog.ts

import { createSlice } from "@reduxjs/toolkit";
import monkeySlice, { ukkiAction } from "./monkeySlice";

const initialState = { x: 456 }

const exampleSlice = createSlice({
    name: 'dog',
    initialState,
    reducers: {

        // dog/ukki
        ukki: (s, x) => {
            console.log(`dog.reducer:ukki = ${x.payload} / ${x.type}`);
        },

        // dog/wan
        wan: (s, x) =>
        {
            console.log(`dog.reducer:wan = ${x.payload} / ${x.type}`);
        }

    },

    extraReducers: (builder) => {

        // ukki
        builder.addCase(
            ukkiAction,
            (s, x) => {
                console.log(`dog.extra:ukki = ${x.payload} / ${x.type}`);
            }
        )

        // monkey/wan
        builder.addCase(
            monkeySlice.actions.wan,
            (s, x) => {
                console.log(`dog.extra:wan = ${x.payload} / ${x.type}`);
            }
        )

        // monkey/ukki
        builder.addCase(
            monkeySlice.actions.ukki,
            (s, x) => {
                console.log(`dog.extra:monkey/ukki = ${x.payload} / ${x.type}`);
            }
        )

    }
})

export default exampleSlice;

store.ts

import { configureStore } from "@reduxjs/toolkit";
import monkeySlice from "./slices/monkeySlice";
import dogSlice from "./slices/dogSlice";

export const store = configureStore({
    reducer: {
        monkey: monkeySlice.reducer,
        dog: dogSlice.reducer
    }
})

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

TestControl.ts

import React from "react"
import { useAppDispatch } from "../redux/hooks"
import monkeySlice, { monkeyUkkiAction, ukkiAction } from "../redux/slices/monkeySlice";

export const TestControl = () => {
    const dispatch = useAppDispatch();

    return (
        <>
            <input type="button"
                value="ukki"
                onClick={e => dispatch(ukkiAction())} />

            <input type="button"
                value="wan"
                onClick={e => dispatch(monkeySlice.actions.wan(null))} />

            <input type="button"
                value="monkey/ukki"
                onClick={e => dispatch(monkeyUkkiAction())} />

        </>
    )
}

前提知識

その前に前提知識。

monkeySlice.tsのこの4行。

console.log(ukkiAction.type); // ukki
console.log(monkeyUkkiAction.type); // monkey/ukki
console.log(monkeySlice.actions.ukki.type); // monkey/ukki
console.log(monkeySlice.actions.wan.type); // monkey/wan

3行目、createSlice()のreducersに追加した場合、スライスのactionsから取得出来ます。
アクションタイプの命名規則はnameプロパティがプレフィックスになります。

namemonkeyで関数がukkiの場合、アクションタイプはmonkey/ukkiです。

reducersにアクションタイプがあるとそちらの関数が実行され、
extraReducersの方は実行されないっぽい。

実行結果

では実行結果。
TestControl.tsxにて。

[ukki]ボタン

dispatch(ukkiAction())

ukkiAction()のアクションタイプはukkiなので呼び出されるのはmonkeyとdogのextractReducersで受け取れる。

monkey.extra:ukki = undefined / ukki
dog.extra:ukki = undefined / ukki

[wan]ボタン

dispatch(monkeySlice.actions.wan(null))

reducersで追加したwanは命名規則によりmonkey/wanになる。

monkey.reducer:wan = null / monkey/wan
dog.extra:wan = null / monkey/wan

monkeySliceではreducersにmonkey/wanがあるので、extractReducers側のmonkey.extra:wanは呼び出されていない。

[monkey/ukki]ボタン

dispatch(monkeyUkkiAction())

アクションタイプはmonkey/ukkiになるので。

monkey.reducer:ukki = undefined / monkey/ukki
dog.extra:monkey/ukki = undefined / monkey/ukki

こちらもreducers側にmonkey/ukkiがあるのでmonkey.extra:monkey/ukkiは呼ばれてないことがわかる。

最後にコード全体。

BlockEditor certificate css DataGrid Docker Gutenberg Hyper-V iframe MUI openssl PHP React ReduxToolkit REST ubuntu WordPress オレオレ認証局 フレームワーク