0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

useReducerの使い方

Posted at

はじめに

useReducer を使って同様のステート管理を行うと、より複雑なステートの更新ロジックを扱う場合に、コードが整理されやすくなります。特に、複数の関連するステート変更が絡み合う場合に有効です。
例えば、ゲームに例えるとHPが一定以下になった時に能力が発動するような機能 が必要な時に重宝します。

useReducer を使ってカウンターの状態管理を行う例を紹介します。ここでは、カスタムフックとコンテキストを組み合わせて、複数のコンポーネントで同じステートを共有するパターンで useReducer を利用します。

サンプル

import React, { createContext, useContext, useReducer, useCallback } from 'react';

// アクションの型を定義
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';

// reducer関数
const counterReducer = (state, action) => {
  switch (action.type) {
    case INCREMENT:
      return { count: state.count + 1 };
    case DECREMENT:
      return { count: state.count - 1 };
    default:
      return state;
  }
};

// カウンターのコンテキストを作成
const CounterContext = createContext(null);

// カウンターのステートとディスパッチ関数を提供するプロバイダー
const CounterProvider = ({ children }) => {
  const [state, dispatch] = useReducer(counterReducer, { count: 0 });

  const increment = useCallback(() => {
    dispatch({ type: INCREMENT });
  }, [dispatch]);

  const decrement = useCallback(() => {
    dispatch({ type: DECREMENT });
  }, [dispatch]);

  return (
    <CounterContext.Provider value={{ count: state.count, increment, decrement, dispatch }}>
      {children}
    </CounterContext.Provider>
  );
};

// カウンターのステートと関数を利用するためのカスタムフック
const useCounterContext = () => {
  const context = useContext(CounterContext);
  if (!context) {
    throw new Error("useCounterContext must be used within a CounterProvider");
  }
  return context;
};

// カウンターを表示する子コンポーネント
const CounterDisplay = () => {
  const { count } = useCounterContext();
  return <div>現在のカウント: {count}</div>;
};

// カウントを増やすボタンを持つ子コンポーネント
const IncrementButton = () => {
  const { increment } = useCounterContext();
  return <button onClick={increment}>増やす</button>;
};

// カウントを減らすボタンを持つ子コンポーネント
const DecrementButton = () => {
  const { decrement } = useCounterContext();
  return <button onClick={decrement}>減らす</button>;
};

// 親コンポーネント
const ParentComponent = () => {
  return (
    <CounterProvider>
      <CounterDisplay />
      <IncrementButton />
      <DecrementButton />
    </CounterProvider>
  );
};

export default ParentComponent;

変更点:

  1. counterReducer 関数:

    • 現在のステートとアクションを受け取り、新しいステートを返します。
    • アクションの type に基づいて、どのようにステートを更新するかを定義します。
    • INCREMENTDECREMENT のアクションを処理します。
  2. CounterProvider コンポーネント:

    • useReducer フックを使って、statedispatch 関数を取得します。初期ステートは { count: 0 } です。
    • increment 関数と decrement 関数は、それぞれ対応するタイプ (INCREMENT, DECREMENT) のアクションを dispatch 関数に渡します。useCallback を使用して、依存配列に dispatch を含めることで、dispatch 関数が変更された場合にのみ再生成されるように最適化しています。
    • コンテキストプロバイダーの値として、state.count と、アクションをディスパッチするための関数 (increment, decrement, dispatch) を提供します。
  3. useCounterContext カスタムフック:

    • コンテキストの値を取得し、それを返します。
  4. 子コンポーネント (CounterDisplay, IncrementButton, DecrementButton):

    • useCounterContext フックを通じて、共有された countincrement, decrement 関数を利用します。

useReducer を使うメリット:

  • 複雑なステート管理: ステートの更新ロジックが複雑になるほど、reducer 関数内でロジックを集中管理できるため、コードの見通しが良くなります。
  • 予測可能なステート遷移: アクションを通じてのみステートが変更されるため、いつ、なぜステートが変更されたのかを追跡しやすくなります。
  • テストの容易性: reducer 関数は純粋な関数であるため、入力(現在のステートとアクション)に対して常に同じ出力(新しいステート)が得られることを保証しやすく、単体テストが容易になります。

useReducer を使うデメリット:

  • ボイラープレートコードが増える: シンプルなステート管理の場合でも、アクションの型定義や reducer 関数の記述が必要になるため、コード量が増えることがあります。

どのような場合に useReducer を使うべきか:

  • ステートの更新ロジックが複数あり、相互に依存している場合。
  • ステートの変更履歴を追跡したい場合(デバッグやUndo/Redo機能の実装など)。
  • より予測可能でテストしやすいステート管理を目指したい場合。

今回のカウンターの例のように、単純なステートの増減だけであれば、useState でも十分にシンプルに実装できます。しかし、より複雑なステート管理が必要になった場合には、useReducer を検討することで、より堅牢で保守性の高いコードを書くことができるでしょう。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?