React のコンポーネントで最も手軽に状態を持てるのは useState です。小さな入力欄やトグルスイッチ程度ならこれで十分です。ですが、アプリが少し大きくなると 「いつ useState だけで済むか」 を見極める必要が出てきます。ここでは、初心者が「状態管理を拡張すべき」サインと、軽量な代替手段を紹介します。
1. useState が限界になるサイン
| サイン | 具体例 |
|---|---|
| 状態が複数コンポーネントで共有 | フォーム全体の入力値を別々の子コンポーネントで扱う |
| 状態更新ロジックが複雑 | カウントアップと同時に条件分岐で別アクションを走らせる |
| パフォーマンスが気になる | 大量リストの選択状態を多数の子に伝播させる |
| テストやデバッグが面倒 | 変更履歴を追いたいが setState が散在している |
これらに当てはまったら、軽量な状態管理ライブラリ の導入を検討しましょう。
2. まずは useReducer でステップアップ
useReducer は useState のシンタックスは変わらないものの、状態遷移を関数で一元管理 できます。小規模なアプリでもロジックが分かりやすくなるので、初心者におすすめです。
import { useReducer } from "react";
type State = { count: number };
type Action = { type: "increment" } | { type: "decrement" };
function reducer(state: State, action: Action): State {
switch (action.type) {
case "increment":
return { count: state.count + 1 };
case "decrement":
return { count: state.count - 1 };
default:
return state;
}
}
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>
</>
);
}
3. さらに軽くしたいときは外部ライブラリ
| ライブラリ | 特徴 |
|---|---|
| Zustand | グローバルストアをシンプルな関数で作成、React の外でも使用可 |
| Jotai | 原子(atom)単位で状態を管理、必要なコンポーネントだけ再描画 |
| Valtio | プロキシベースで直感的にミュータブルに書けるが、内部でイミュータブルに変換 |
これらは 依存が数KB程度 と非常に軽量です。useState と同様にフックで呼び出すだけなので、学習コストも低めです。
4. まとめのチェックリスト
- 状態が 1つのコンポーネントだけ に留まる →
useStateOK - 複数コンポーネントで 同じデータを共有 →
useReducerか Zustand - 状態更新ロジックが 条件分岐や複数ステップ →
useReducerが見やすい - パフォーマンスやコードベースが 大規模 → Jotai / Valtio で粒度を調整
初心者でも段階的に状態管理を拡張できるので、まずはサインを見極めてから次の手段に移行しましょう。