🍑

Redux Toolkit スライスの基本: 初期状態、リデューサー、extraReducers の使い方

2024/08/11に公開

スライスとは、Redux Toolkitが提供する機能の一つで、アプリケーションの状態管理を簡単に行うためのもの。スライスは、機能やドメインに関連する状態、リデューサー、対応するアクションを一つのまとまりとして管理し、特に比較的大規模なアプリケーションでよく使用される。

Redux Toolkit と RTK Query: 基本概要についてはこちらにまとめました。

スライスの具体的な機能

1. 初期状態の設定

スライス内で管理する状態の初期値を定義する。

2. リデューサーの定義

状態を変更するための関数(リデューサー)をまとめて定義する。

3. extraReducersの利用

スライス外のアクションや非同期処理の結果に基づいて状態を更新する処理を定義する機能。

4. アクションの自動生成

アクションクリエーターによって、リデューサーに対応するアクションが自動生成され、状態の操作が容易になる。リデューサーを書けば、アクションを別に書かなくて良い、というのもRedux Toolkitの利点である。

5. リデューサーのエクスポート

エクスポートされたリデューサーは、ストアに統合され、アプリケーション全体で管理される状態の一部となる。

スライスの実装例

exampleSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { userAPI } from '@services/user';

// TypeScriptの型定義:initialState で管理する状態の型を定義する
interface ExampleState {
  userId: string | null;
  organizationId: string | null;
  status: 'idle' | 'loading' | 'loaded';
}
// initialStateの定義
const initialState: ExampleState = {
  userId: null,
  organizationId: null,
  status: 'idle',
};

//スライスはcreateSliceで作成する
const exampleSlice = createSlice({
  name: 'example',
  initialState,
  // reducers の定義
  reducers: {
    // setUserId、setOrganizationId はリデューサー名であり、アクション名である
    // アクションクリエーターによってアクションが自動生成されている
    setUserId(state, action: PayloadAction<string>) {
      state.userId = action.payload;
    },
    setOrganizationId(state, action: PayloadAction<string>) {
      state.organizationId = action.payload;
    },
  },
  //extraReducers の定義(スライス外のアクションの結果に基づいて更新される)
  extraReducers: (builder) => {
    // addMatcherを使用してこのスライス外のAPIが呼び出されたときにリデューサーを実行する
    builder.addMatcher(
        // userAPI.endpoints.getUserStatus.matchFulfilled は、
        // getUserStatus API の呼び出しが成功したときに発生するアクション
        userAPI.endpoints.getUserStatus.matchFulfilled,
        (state, { payload }) => {
          const user = payload.user;
          const organization = user.organizations.find(
            // ユーザーのデフォルト組織IDに一致する組織を取得
            (org) => org._id === user.defaultOrganization
          );
          state.organizationId = organization?._id;
    
          // 新しいデータが取得された場合にステータスを更新
          if (state.userId !== user._id) {
            state.userId = user._id;
          }
    
          state.status = 'loaded';
    });
  },
});

// アクションのエクスポート(これにより、他のコンポーネントやファイルで
// これらのアクションを使用して、状態を更新できる)
export const { setUserId, setOrganizationId } = exampleSlice.actions;

// reducer は、createSlice によって生成されたリデューサー関数。
// これは、reducers オブジェクトに定義されたすべてのリデューサーを含む1つのリデューサー関数
// としてエクスポートされる。この reducer は、ストアに組み込むために使用される。
export default exampleSlice.reducer;

リデューサーのエクスポートとストアへの組み込み実装例

import { configureStore } from '@reduxjs/toolkit';
// exampleSlice.reducer をインポートする
import exampleSlice from './exampleSlice'; 

// ストアの設定
const store = configureStore({
  reducer: {
    // exampleSlice.reducer をストアに組み込み、
    // アプリケーション全体で使用される状態管理の一部として組み込まれる
    example: exampleSlice.reducer, 
  },
});

export default store;

コンポーネントでの使用例

import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { setUserId, setOrganizationId } from './exampleSlice';

const ExampleComponent = () => {
  const dispatch = useDispatch();

  // useSelectorを使ってストアから状態を取得する
  const userId = useSelector((state) => state.example.userId);
  const organizationId = useSelector((state) => state.example.organizationId);

  // setUserId や setOrganizationId アクションをディスパッチし、状態を更新する
  const handleSetUserId = () => {
    // dispatchを使用。新しいuserIdを設定する
    dispatch(setUserId('newUserId123')); 
  };

  const handleSetOrganizationId = () => {
    // dispatchを使用。新しいorganizationIdを設定する
    dispatch(setOrganizationId('newOrganizationId456')); 
  };

  return (
    <div>
      <h1>User ID: {userId}</h1>
      <h2>Organization ID: {organizationId}</h2>
      {/* ボタンをクリックして状態を更新 */}
      <button onClick={handleSetUserId}>Set User ID</button>
      <button onClick={handleSetOrganizationId}>Set Organization ID</button>
    </div>
  );
};

export default ExampleComponent;

Discussion