第69章:useReducer と useState、どっち使う?
この章は、設計のセンスを育てる回です ✨
「とりあえず useState で書いちゃうけど、useReducer を使った方がよかったのかな…?」
そんなモヤモヤをスッキリさせましょう 🧠💡
1. この章のゴール 🎯
この章が終わるころには、
- 「これは 絶対
useStateでいいやつ」 - 「これはそろそろ
useReducerにした方が安全かも」
が、自分で判断できるようになるのがゴールです ✌️
2. まずは共通点をサクッと整理 🧩
useState も useReducer も、どちらも
「コンポーネントの“状態(state)”を覚えておくためのフック」
です。React公式ドキュメントでも、 どちらも「State Hooks」グループとして紹介されています。(React)
ざっくりいうとこんな感じ👇
-
useState- 値そのものと、それを変える関数を返す
- 「その場でサクッと更新」するイメージ
-
useReducer- 値(state)と、「何が起きたか」を投げる
dispatch関数を返す - 「ルール(reducer関数)」にしたがって更新するイメージ(React)
- 値(state)と、「何が起きたか」を投げる
どっちも「状態を持つ」ための道具で、 どっちが“えらい”とかはないです。 大事なのは「どんな状態を扱いたいか」👀
3. useState が得意なパターン 🎀
まずは useState のおいしい使いどころから 🌸
👍 向いているケース
-
状態がシンプル
- 例:
boolean,number,string1〜2個くらい - 「モーダル開いてるかどうか」「カウント数」など
- 例:
-
更新ルールが単純
- 例:
+1するだけ、true/falseを切り替えるだけ - 「ボタンを押したらフラグを反転」「フォームの入力値をそのまま入れる」
- 例:
-
状態どうしの関係がほぼない
isOpenとcountが全然関係ない…みたいな
React公式ガイドでも、
シンプルな状態管理には useState を使うのが基本だよと説明されています。(legacy.reactjs.org)
🌼 例1:シンプルなモーダル(useReducer はまだいらない)
import { useState } from "react";
export function SimpleModal() {
const [isOpen, setIsOpen] = useState(false);
return (
<div>
<button onClick={() => setIsOpen(true)}>
詳細をひらく 📖
</button>
{isOpen && (
<div>
<p>ここに詳細テキストが入ります ✨</p>
<button onClick={() => setIsOpen(false)}>
とじる
</button>
</div>
)}
</div>
);
}
このくらいなら、useState 一択でOKです 🙆♀️
4. useReducer が得意なパターン 🧠
次は useReducer の出番が増えてくるケース ✍️
React公式やいろんな解説記事でも、共通してだいたいこう説明されています:
「状態が複雑になってきたら
useReducerを検討しよう」(legacy.reactjs.org)
💪 向いているケース
-
値の種類が多くて、ひとまとまりで意味がある
-
例:ユーザー情報フォーム
name,email,age,addressなど
-
それぞれ別々に
useStateで持つと、更新ロジックがバラバラになりがち
-
-
「こういう操作をしたら、状態はこう変わる」という“ルール”が多い
-
例:
- 「入力を変更した」
- 「サーバーからデータを読んできた」
- 「リセットボタンを押した」
-
こういう「イベント」が増えてくると、 ひとつの
reducerに「ルール」としてまとめた方がスッキリ ✨
-
-
前の状態をもとに次の状態を決める
- 例:カウンターを複雑に増減させる
- 「今の値から〇〇引いて、条件によっては別のフラグも変える」みたいなやつ
-
複数のイベントハンドラが、似たような更新をする
handleChangeNamehandleChangeEmailhandleResetみたいな関数が増えてきたら、reducerで一元管理した方が読みやすいことが多いです。(React)
-
あとで Context と組み合わせて“アプリ全体の状態”にしたくなりそう
dispatchを Context 経由で配るパターンはよく使われます。(React)
🌟 例2:ちょっと複雑なフォーム(useReducer が活きるパターン)
「名前・メール・年齢」を持つフォームを考えてみます。
これを全部 useState で書くと、だんだんイベントハンドラが増えて大変…というケース。
import { useReducer } from "react";
type FormState = {
name: string;
email: string;
age: string;
};
type FormAction =
| { type: "changeField"; field: keyof FormState; value: string }
| { type: "reset" };
const initialState: FormState = {
name: "",
email: "",
age: "",
};
function formReducer(state: FormState, action: FormAction): FormState {
switch (action.type) {
case "changeField":
return {
...state,
[action.field]: action.value,
};
case "reset":
return initialState;
default:
// TS的にはここには来ない想定!
return state;
}
}
export function ProfileForm() {
const [state, dispatch] = useReducer(formReducer, initialState);
return (
<form>
<div>
<label>
名前:
<input
value={state.name}
onChange={(e) =>
dispatch({
type: "changeField",
field: "name",
value: e.target.value,
})
}
/>
</label>
</div>
<div>
<label>
メール:
<input
value={state.email}
onChange={(e) =>
dispatch({
type: "changeField",
field: "email",
value: e.target.value,
})
}
/>
</label>
</div>
<div>
<label>
年齢:
<input
value={state.age}
onChange={(e) =>
dispatch({
type: "changeField",
field: "age",
value: e.target.value,
})
}
/>
</label>
</div>
<button
type="button"
onClick={() => dispatch({ type: "reset" })}
>
リセット 🔁
</button>
</form>
);
}
💡ポイント
-
「状態をどう変えるか」のルールは
formReducerに全部集約 -
画面側は「何が起きたか(Action)」だけ投げる
type: "changeField"type: "reset"
フォーム項目が増えたり、
「サーバーから読み込んだ値で全部書き換える」
みたいな動きが増えても、reducer を見れば全体の動きが分かるのが強みです 💪
5. ざっくり判断フローチャート 🌈
「どっち使う?」を図でイメージしてみましょう 🧭
「ちょっと複雑になってきたな…」と感じたら
useReducerに“昇格”させるタイミングかも、くらいの気持ちで大丈夫です 😊
6. 実務寄りな「3つのチェックリスト」✅
コードを書いていて迷ったとき、 この3つを自分に質問してみてください ✋
Q1. 「状態の数」と「関連性」はどう?
- ✅ 状態が少ない / 互いにあまり関係ない
→
useStateでOK - ⚠️ プロパティがたくさんあって、
「この値が変わったら、あれも一緒に直さないと…」が増えてきた
→
useReducerを検討
Q2. 更新ロジックの説明、口でスラスラ言える?
- ✅ 「ボタン押したら +1 」「押したら開く・閉じる」レベル
→
useState - ⚠️ 「このボタンだと… A の値がこうで、B も変わって、条件によっては C も…」と
口頭説明が長くなる
→ ルールを
reducerに“日本語のif文”みたいにまとめた方が安全
Q3. この状態、あとで別のコンポーネントにも共有したくなりそう?
- ✅ 1つのコンポーネントの中だけで完結しそう
→
useState - ⚠️ Context にのせてアプリ全体で共有しそう
→
useReducer + Contextの組み合わせは鉄板パターン 🔧
7. 「迷ったらこうする」ガイドライン 💖
最後に、超シンプルなマイルール案です。 (慣れてきたら自分なりにアレンジしてOK!)
-
最初は素直に
useStateから始める -
こんなサインが出てきたら
useReducerを検討 👇setXxxが増えすぎてきた- 関数名が
handleChangeName,handleChangeEmail,handleChangeAge…と増殖 - バグが「状態が食い違ってる」系で出てくる
-
「これはフォーム全体のルールっぽいな」「一連の操作の“ストーリー”があるな」 →
useReducerで“物語(Action)とルール(Reducer)”に整理する
8. ミニ練習問題 ✍️✨
「これ、あなたならどっちを選ぶ?」を考えてみてください 🧠
-
いいねボタン
- 押したら
likedフラグがtrue/falseになるだけ - →
useStateoruseReducer?
- 押したら
-
買い物カゴ
- 商品を追加・削除
- 合計金額も一緒に更新
- 「全部削除」ボタンもある
- → どっち?
-
プロフィール編集画面
- 名前・メール・自己紹介文・アイコン画像URL
- 「サーバーから読み込んだ値で初期化」「変更を保存」「リセット」
- → どっち?
💬 もし答えに迷ったら、「こう思ったんだけど合ってる?」って聞いてくれてOKです 🌟 一緒に「設計の勘どころ」を育てていきましょ〜 🐣💻💖