0Pricing
Frontend Academy · レッスン

複雑な状態のための useReducer

reducer 関数で状態遷移をモデル化し、アクションオブジェクトを dispatch し、状態ロジックが複雑になったら useState より useReducer を優先します。

「複雑な状態のための useReducer」はCoddyKit上の無料Frontend Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはFrontend Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Frontend Academyコースには全4レッスンが含まれています。

useStateでは足りない場合

状態の遷移に複雑なロジックや複数の関連する値が含まれる場合、または新しい状態が以前の状態に単純ではない形で依存する場合は、複数のuseStateを呼び出すよりもuseReducerを使うほうがすっきりします。

Reducer関数のパターン

Reducerは純粋関数です: (state, action) => newState。現在の状態とアクションオブジェクトを受け取り、次の状態を返します。ミューテーションは行わず、必ず新しいオブジェクトを返します。

type Action =
  | { type: 'increment' }
  | { type: 'decrement' }
  | { type: 'reset'; payload: number };

function counterReducer(state: number, action: Action): number {
  switch (action.type) {
    case 'increment': return state + 1;
    case 'decrement': return state - 1;
    case 'reset':     return action.payload;
    default: return state;
  }
}

useReducerの構文

useReducer(reducer, initialState)は、現在の状態とdispatch関数を返します。アクションオブジェクトを指定してdispatchを呼び出すと、状態が遷移します。

function Counter() {
  const [count, dispatch] = useReducer(counterReducer, 0);

  return (
    <div>
      <p>{count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
      <button onClick={() => dispatch({ type: 'reset', payload: 0 })}>Reset</button>
    </div>
  );
}

型付きアクション — 判別可能なユニオン型

アクションにはTypeScriptの判別可能なユニオン型を使うと、reducerのswitchで網羅的な型チェックを行えます。

type CartAction =
  | { type: 'ADD_ITEM'; item: CartItem }
  | { type: 'REMOVE_ITEM'; id: string }
  | { type: 'SET_QUANTITY'; id: string; qty: number }
  | { type: 'CLEAR' };

複雑な状態の例: ショッピングカート

追加、削除、数量更新の操作を持つショッピングカートは、useReducerの使用例として最適です。

interface CartState {
  items: CartItem[];
  total: number;
}

function cartReducer(state: CartState, action: CartAction): CartState {
  switch (action.type) {
    case 'ADD_ITEM': {
      const exists = state.items.find(i => i.id === action.item.id);
      if (exists) {
        const items = state.items.map(i =>
          i.id === action.item.id ? { ...i, qty: i.qty + 1 } : i
        );
        return { ...state, items, total: calcTotal(items) };
      }
      const items = [...state.items, { ...action.item, qty: 1 }];
      return { ...state, items, total: calcTotal(items) };
    }
    case 'CLEAR': return { items: [], total: 0 };
    default: return state;
  }
}

dispatchは安定している

dispatch関数は安定しています。レンダリングの間で変わらないため、再実行を引き起こすことなくpropsとして渡したり、useEffectの依存配列で使ったりできます。

useReducerとuseContextの組み合わせ

コンテキストから状態とdispatchの両方を提供する強力なパターンです。これにより、どのコンシューマーからでもプロップドリリングなしで状態を読み取ったり、アクションをディスパッチしたりできます。

const CartContext = createContext<{ state: CartState; dispatch: Dispatch<CartAction> } | null>(null);

function CartProvider({ children }: { children: React.ReactNode }) {
  const [state, dispatch] = useReducer(cartReducer, { items: [], total: 0 });
  return <CartContext.Provider value={{ state, dispatch }}>{children}</CartContext.Provider>;
}

useReducerとuseStateの比較

次のような場合はuseReducerを選びます: 状態に複数のサブ値がある、次の状態が以前の状態に複雑な形で依存する、状態遷移が多く明示的な名前を付けたい、またはコンポーネントから状態ロジックを分離してテストしたい場合です。

Reducerを単独でテストする

Reducerは純粋関数なので、コンポーネントをレンダリングせずに簡単にテストできます。

test('increment action increases count', () => {
  expect(counterReducer(0, { type: 'increment' })).toBe(1);
  expect(counterReducer(5, { type: 'increment' })).toBe(6);
});

test('reset action sets count to payload', () => {
  expect(counterReducer(42, { type: 'reset', payload: 0 })).toBe(0);
});

不変更新のためのImmer

immerライブラリを使うと、ミューテーションを行っているように見えるコードで、新しい不変の状態を生成できます。Reducerで複雑なネストされたオブジェクトを更新するときに便利です。

理解度チェック

Reducerの関数シグネチャはどのようなものですか?

復習: useReducer

(state, action) => newState — 純粋関数であり、ミューテーションは行いません。useReducer(reducer, initial)は[state, dispatch]を返します。安全性を高めるには、判別可能なユニオン型のアクションを使います。dispatchは安定しています。状態遷移が多い複雑な状態に適しています。グローバル状態管理にはuseContextと組み合わせます。Reducerは純粋関数として単独でテストできます。

よくある質問

「複雑な状態のための useReducer」レッスンは無料ですか?

はい。「複雑な状態のための useReducer」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Frontend Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Frontend Academyコースには全4レッスンが含まれています。

「複雑な状態のための useReducer」で何を学びますか?

reducer 関数で状態遷移をモデル化し、アクションオブジェクトを dispatch し、状態ロジックが複雑になったら useState より useReducer を優先します。 ブラウザで直接実行するハンズオンコードでFrontend Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Frontend Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのFrontend Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「複雑な状態のための useReducer」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このFrontend Academyレッスンでコードを書いて実行できますか?

はい。すべてのFrontend Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. グローバル状態のための useContext
  2. 複雑な状態のための useReducer
  3. パフォーマンスのための useMemo と useCallback
  4. カスタム Hook:再利用可能なロジックの抽出
← Frontend Academyに戻る