なぜuseReducerが必要か
useStateは直感的で便利ですが、
- 複数の状態が相互に影響する
- 更新ロジックが長い if/else で肥大化する
- 次の状態が前の状態に依存する更新が多い
といった場面では読みづらくなります。こうしたケースでuseReducerを使うと、状態更新ロジックを1カ所に集約でき、コンポーネントがスッキリします。
基本シグネチャ
const [state, dispatch] = useReducer(reducer, initialState, init?)| パラメータ | 型 | 意味 |
|---|---|---|
reducer | (state, action) ⇒ newState | 更新ロジックを持つ純粋関数 |
initialState | 任意 | 初期値 |
init | (optional) (arg) ⇒ initializedState | 遅延初期化用 |
戻り値
state:現在の状態dispatch:アクションを送る関数
まずはカウンターで理解
import { useReducer } from 'react';
function reducer(state, action) {
switch (action.type) {
case 'increment': return { count: state.count + 1 };
case 'decrement': return { count: state.count - 1 };
default: throw new Error();
}
}
export default function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>−</button>
</>
);
}ポイント解説
reducerは 純粋関数。副作用を入れないdispatchへは オブジェクトを渡す(type必須)- UI 側は state を読むだけ=ロジックが分離され可読性↑
複数フィールドのフォーム例
useStateでそれぞれ管理すると更新がバラけますが、useReducerで一元化可能。
function formReducer(state, action) {
switch (action.type) {
case 'change':
return { ...state, [action.field]: action.value };
case 'reset':
return action.initial;
default:
return state;
}
}
const initialForm = { name: '', email: '', password: '' };
function SignupForm() {
const [form, dispatch] = useReducer(formReducer, initialForm);
const handleChange = e =>
dispatch({ type: 'change', field: e.target.name, value: e.target.value });
return (
<form>
{Object.entries(form).map(([key, val]) => (
<input key={key} name={key} value={val} onChange={handleChange} />
))}
<button onClick={() => dispatch({ type: 'reset', initial: initialForm })}>
クリア
</button>
</form>
);
}深掘り
...stateで既存をコピー=不変性を守るresetアクションでワンアクション全項目初期化- 大量フィールドでも reducer が変わらないので拡張に強い
遅延初期化でパフォーマンス改善
重い計算で初期値を作る場合、第三引数 init を使うとレンダー時に毎回呼ばれません。
function init(countFromLocal) {
return { count: countFromLocal };
}
const [state, dispatch] = useReducer(reducer, initialArg, init);useState vs useReducer 使い分け指針
| シチュエーション | 推奨フック |
|---|---|
| 1〜2個の単純な値 | useState |
| 更新ロジックが直感的 | useState |
| 多数のフィールド・複雑な分岐 | useReducer |
| 次の状態が前の状態に強く依存 | useReducer |
| Redux 風のアーキテクチャを局所的に実現 | useReducer |
コンポーネント分割との相性
Reducer を別ファイルに切り出し、複数コンポーネントから共通利用するとReduxライクな構成が得られます。
// todoReducer.js
export function todoReducer(state, action) { /* 省略 */ }
// TodoList.jsx
import { todoReducer } from './todoReducer';
const [todos, dispatch] = useReducer(todoReducer, []);まとめ
useReducerは状態更新ロジックを一元管理し、可読性と拡張性を両立させる強力なフックです。
- 純粋関数
reducerを定義 dispatchで明示的にアクションを送る- ステートが複雑化したら
useReducerへ移行
ロジックが整理されることでバグ発生率が減り、大規模アプリでもスケールしやすいコードベースを実現できます。




コメント