React 速習チュートリアル

React useReducer Hook

1. useReducer

useReducer フックは、useState フックと似た性質を持っています。

大きな違いは、カスタムのステートロジック(State Logic) を構築できる点にあります。
もし、複雑なロジックに依存する複数のステートを同時に追跡・管理する必要がある場合、useReducer を使用するのが非常に有効です。

2. 構文 (Syntax)

useReducer フックは3つの引数を受け取ります。

useReducer(reducer, initialState, init)

  • reducer: あなたのカスタムステートロジックを記述した関数です。
  • initialState: 初期ステートを指定します。単純な値でも構いませんが、一般的にはオブジェクト(Object)が使用されます。
  • init: ステートを初期化するために使用されるオプショナル(任意)の引数です。

useReducer フックは、現在のステート(state)ディスパッチメソッド(dispatch method) を返します。

3. 実装例

以下は、useReducer を使用して2人のプレイヤーのスコアを追跡する例です。

import { useReducer } from 'react';
import { createRoot } from 'react-dom/client';

// 1. 初期ステートの定義
const initialScore = [
  {
    id: 1,
    score: 0,
    name: "ジョン",
  },
  {
    id: 2,
    score: 0,
    name: "サリー",
  },
];

// 2. リデューサー関数の定義
const reducer = (state, action) => {
  switch (action.type) {
    case "INCREASE":
      // 対象のプレイヤーのスコアのみを +1 する
      return state.map((player) => {
        if (player.id === action.id) {
          return { ...player, score: player.score + 1 };
        } else {
          return player;
        }
      });
    default:
      return state;
  }
};

function Score() {
  // 3. useReducerを初期化
  const [score, dispatch] = useReducer(reducer, initialScore);

  const handleIncrease = (player) => {
    // 4. アクションをディスパッチしてステートを更新
    dispatch({ type: "INCREASE", id: player.id });
  };

  return (
    <>
      {score.map((player) => (
        <div key={player.id}>
          <label>
            <input
              type="button"
              onClick={() => handleIncrease(player)}
              value={player.name}
            />
            <span> スコア: {player.score}</span>
          </label>
        </div>
      ))}
    </>
  );
}

createRoot(document.getElementById('root')).render(
  <Score />
);

3.1 実装のポイント

この例では、プレイヤーの名前が書かれたボタンをクリックすると、dispatch 関数が呼ばれます。この関数は「何をしたいか」という指示(アクション)をリデューサー(reducer)に送り、リデューサーが新しいステートを計算して返します。

useState よりも記述量は増えますが、ステートの更新ロジックをコンポーネントの外部に切り出せるため、コードが読みやすく、テストしやすくなるというメリットがあります。