Reactのコンポーネントで最も基本的なのは useState ですが、規模が大きくなると 「どこで状態管理を切り替えるか」 が悩みどころです。この記事では、初心者が useState だけで抱えがちな課題 を把握し、軽量な代替手段(useReducer、Zustand、Jotai など)を 導入すべき具体的なタイミング を 5 つに絞って解説します。
1. 同一コンポーネントで状態が増えすぎたとき
useState を 10 個以上書くケースは少なくありません。
- 症状: 変数名が増えてコードが読みにくい
- 判断基準: 同じコンポーネント内で 3 つ以上のロジックが状態を共有する場合
対策: useReducer を使うと、状態遷移が一元管理でき、アクション名で意図が明確になります。
import React, { useReducer } from "react";
type State = { count: number; text: string };
type Action =
| { type: "increment" }
| { type: "decrement" }
| { type: "setText"; payload: string };
const reducer = (state: State, action: Action): State => {
switch (action.type) {
case "increment":
return { ...state, count: state.count + 1 };
case "decrement":
return { ...state, count: state.count - 1 };
case "setText":
return { ...state, text: action.payload };
default:
return state;
}
};
export const Counter = () => {
const [state, dispatch] = useReducer(reducer, { count: 0, text: "" });
return (
<>
<p>{state.count}</p>
<button onClick={() => dispatch({ type: "increment" })}>+</button>
<button onClick={() => dispatch({ type: "decrement" })}>-</button>
<input
value={state.text}
onChange={(e) => dispatch({ type: "setText", payload: e.target.value })}
/>
</>
);
};
2. 複数コンポーネントで同じ状態を共有したい
親子関係が深くなると props の掘り下げが頻発します。
- 症状: 「props drilling」 が目立つ
- 判断基準: 2 つ以上の子コンポーネントが同一状態を参照・更新する場合
対策: Zustand のようなシンプルなグローバルストアを導入すると、コンテキストや Redux のボイラープレートなしに共有できます。
3. 非同期ロジックと状態が絡むとき
API 呼び出しの結果を複数箇所で利用するケース。
- 症状: ローディングやエラーハンドリングが散在する
- 判断基準: 同一データ取得ロジックが 2 つ以上のコンポーネントで必要になるとき
対策: Jotai の atom と useAtom を組み合わせると、非同期関数を atom にラップでき、どこからでも同じデータにアクセス可能です。
4. 型安全を保ちつつ状態を拡張したい
TypeScript で型エラーが頻発する場合。
-
症状:
anyを多用せざるを得ない - 判断基準: 状態オブジェクトに対して複数の型が混在し、型推論が失われるとき
対策: useReducer の型パラメータを活用すれば、アクションごとに厳密な型を定義でき、将来的なリファクタリングが楽になります。
5. パフォーマンスが気になるが、導入コストは抑えたい
大量のリストや頻繁な再レンダリングがボトルネックになるケース。
- 症状: コンポーネントが不要に再描画される
-
判断基準:
React.memoだけでは解決できず、状態更新が頻繁に走るとき
対策: useReducer と useCallback を組み合わせ、状態更新ロジックをメモ化するだけで、ほとんどのケースで十分です。重いフレームワーク(Redux Toolkit 等)を導入する必要はありません。
まとめ
-
小規模 なら
useStateが最速 -
状態が増える、共有が必要になる、非同期が絡む ときは 軽量ストア(
useReducer、Zustand、Jotai)へシフト - 型安全とパフォーマンスは React のフックだけでも十分 に担保できる
実際に手を動かしてみると、どのタイミングで切り替えるか感覚がつかめます。まずは useReducer を試し、必要に応じて外部ストアを検討してみてください。