[React.js] - パート13: React Hooks - useReducer Hook

Ace Lennox
ReactのuseReducer
フックは、useState
の強力な代替手段であり、複雑な状態ロジックを管理するのに役立ちます。このガイドでは、useReducer
の構文と用途を分かりやすい例を使って詳しく解説します。
useReducer
とは?
useReducer
フックは、カスタムロジックを使って状態を管理する方法を提供します。特に次のような場合に有効です:
複数の値を含む状態管理が必要なとき。
状態更新が複雑なロジックに依存する場合。
useState
が単純なアップデート関数を使用するのに対し、useReducer
では状態更新を一元化して管理するリデューサー関数を使用します。これにより、コードの可読性とメンテナンス性が向上します。
構文
useReducer
フックは、次の2つの引数を受け取ります:
リデューサー関数:ディスパッチされたアクションに基づいて状態変更を決定します。
初期状態:状態の初期値。単純な値またはオブジェクトとして設定できます。
const [state, dispatch] = useReducer(reducer, initialState);
state
: 現在の状態値。dispatch
: 状態更新をトリガーするための関数。
例1: シンプルなカウンター
まず、useReducer
を使ってシンプルなカウンターアプリを作成します:
import { useReducer } from "react";
import ReactDOM from "react-dom/client";
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case "INCREMENT":
return { count: state.count + 1 };
case "DECREMENT":
return { count: state.count - 1 };
case "RESET":
return { count: 0 };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<h1>カウント: {state.count}</h1>
<button onClick={() => dispatch({ type: "INCREMENT" })}>増やす</button>
<button onClick={() => dispatch({ type: "DECREMENT" })}>減らす</button>
<button onClick={() => dispatch({ type: "RESET" })}>リセット</button>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Counter />);
この例では、状態の複数の遷移(増加、減少、リセット)を単一の関数で管理しています。
例2: ショッピングカート管理
次に、実用的な例としてショッピングカートアプリを作成します:
import { useReducer } from "react";
import ReactDOM from "react-dom/client";
const initialCart = [];
function cartReducer(state, action) {
switch (action.type) {
case "ADD_ITEM":
return [...state, { id: action.id, name: action.name, quantity: 1 }];
case "REMOVE_ITEM":
return state.filter((item) => item.id !== action.id);
case "INCREASE_QUANTITY":
return state.map((item) =>
item.id === action.id
? { ...item, quantity: item.quantity + 1 }
: item
);
case "DECREASE_QUANTITY":
return state.map((item) =>
item.id === action.id && item.quantity > 1
? { ...item, quantity: item.quantity - 1 }
: item
);
default:
return state;
}
}
function ShoppingCart() {
const [cart, dispatch] = useReducer(cartReducer, initialCart);
const addItem = (id, name) => {
dispatch({ type: "ADD_ITEM", id, name });
};
return (
<div>
<button onClick={() => addItem(1, "りんご")}>りんごを追加</button>
<button onClick={() => addItem(2, "バナナ")}>バナナを追加</button>
<h2>ショッピングカート:</h2>
{cart.map((item) => (
<div key={item.id}>
<p>
{item.name} (x{item.quantity})
<button onClick={() => dispatch({ type: "INCREASE_QUANTITY", id: item.id })}>+</button>
<button onClick={() => dispatch({ type: "DECREASE_QUANTITY", id: item.id })}>-</button>
<button onClick={() => dispatch({ type: "REMOVE_ITEM", id: item.id })}>削除</button>
</p>
</div>
))}
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<ShoppingCart />);
この例では、複数のアクション(追加、削除、数量の増減)を一元管理するcartReducer
を使用しています。
なぜuseReducer
を使うのか?
useReducer
を使う理由として以下が挙げられます:
ロジックの一元化: 状態の遷移ロジックを1つの関数にまとめることで、メンテナンス性が向上します。
拡張性: アプリが拡大しても、ロジックを簡単に追加できます。
予測可能な更新: リデューサーが状態更新を一貫して処理します。
ベストプラクティス
純粋なリデューサー関数を保つ: APIコールなどの副作用はリデューサーに含めず、
useEffect
を使用しましょう。明確なアクションタイプを使用: アクションタイプをわかりやすい名前にすることで、リデューサーのロジックを理解しやすくなります。
関連状態をグループ化: 複数の関連する状態をオブジェクトとして管理すると整理しやすくなります。
まとめ
useReducer
フックは、複雑な状態遷移を必要とするアプリケーションで非常に役立つツールです。このフックを習得することで、Reactアプリケーションの状態管理をより効率的かつスケーラブルにすることができます。
この記事で紹介した例を試して、useReducer
の便利さをぜひ実感してください!
ソースコード
このプロジェクトの完全なソースコードはGitHubで利用可能です。