React状態管理の深掘り #6 どのツールをいつ使うか — 決定ガイド
シリーズ最後の記事です。#1で状態をクライアント状態とサーバー状態に分け、#2から#5までツールを1つずつ見ました。この記事では、それらすべてを1枚の決定フローにまとめます。新しい画面を作るとき、実際にどんな順序で自問すればよいかが目標です。
真っ先に投げる問い #
#1の結論をもう一度確認します。どんな状態に出会っても、最初の問いは1つです。
「この値はブラウザの中で完結するのか、それともサーバーデータの複製なのか?」
この1つの問いが半分を分けます。
- サーバーデータの複製なら → サーバー状態です。グローバルストアに入れず、TanStack Queryに任せます。
- ブラウザの中で完結するなら → クライアント状態です。次の問いへ進みます。
決定フロー1枚 #
この値は何か?
│
├─ サーバーデータの複製(一覧・詳細・検索結果)
│ → TanStack Query (#2)
│ キャッシュ・再取得・鮮度はツールに任せる
│
└─ ブラウザの中で完結するクライアント状態
│
├─ 1つのコンポーネント(または直下の子)だけで使う
│ → useState / useReducer(組み込み)
│
├─ 複数のコンポーネントで共有するが値がほぼ変わらない
│ (テーマ、ロケール、ログインユーザー情報など)
│ → useContext で十分
│
└─ 複数のコンポーネントで共有し、頻繁に変わる
│
├─ 軽く、ボイラープレート最小で
│ → Zustand (#3) ── 1つのストア + セレクター
│ → Jotai (#4) ── 小さな原子 + 派生原子
│
└─ 大きなチーム・厳格な変更規約・強い追跡性が必要
または、すでにReduxのコードベース
→ Redux Toolkit (#5)組み込みを飛ばさないこと #
ツールを5つも見ましたが、最も多く使うことになるのは依然としてuseStateです。新しいライブラリは、複数のコンポーネントで共有 + ツリーで遠く離れているという条件が重なったときに、はじめて価値を持ちます。その前は、組み込みが最もシンプルで最も速いです。
ツールを1つ増やすこと自体が、バンドルサイズ、学習コスト、新しいチームメンバーの参入障壁というコストを生みます。「この状態に本当にグローバルストアが必要か」をまず疑う習慣が、良い設計につながります。
Zustand vs Jotai — 何で分けるか #
どちらも軽くProviderがないので、直接比較されることが多いです。#3と#4で見た違いを1つの表にまとめます。
| 基準 | Zustand | Jotai |
|---|---|---|
| モデル | トップダウン — 1つのストア | ボトムアップ — 小さな原子を組み立て |
| よく合う状態 | ひとかたまりに束ねられるグローバル状態 | 互いに依存する派生値が多い状態 |
| 再レンダリング制御 | セレクターで部分購読 | 原子単位で自然に分離 |
| 使う感覚 | 「グローバルストアを1つ置く」 | 「useStateをグローバルに分けて使う」 |
正解はありません。状態が自然にひとかたまりに束ねられるならZustandがすっきりし、値同士が参照し合って派生する構造ならJotaiのほうがすっきりします。チームが慣れているほうを選ぶのも、十分に妥当な基準です。
よくある落とし穴5つ #
シリーズを貫いて繰り返し強調した間違いを集めました。
1. サーバーデータをグローバルクライアントストアに入れる。 最もよくあり、コストの大きい間違いです。キャッシュと再取得を手で作り直すことになります。サーバー状態はTanStack Query(またはRTK Query)に任せます。
2. すべての状態を1つの巨大なグローバルオブジェクトに集める。 1箇所が変わるとき、関係のないコンポーネントまで再レンダリングされやすいです。セレクターや原子単位で状態を分け、購読を狭めます。
3. ローカル状態までグローバルに引き上げる。 フォーム入力1つ、トグル1つはuseStateで十分です。グローバル化は、共有が本当に必要なときだけにします。
4. ツールを混ぜすぎる。 1つのアプリにReduxとZustandとJotaiを同時に置くと、新しいメンバーが「この状態はどこにあるのか」を毎回探し回ります。クライアント状態のツールは1つに決めるほうがよいです。
5. 再レンダリングを測らずに推測する。 「遅そうだから」とツールを変える前に、React DevTools Profilerで実際の再レンダリングを確認してください。たいてい問題はツールではなく購読の範囲にあります。
現実的な組み合わせ #
実務でよく見る安定した組み合わせは単純です。
サーバー状態 → TanStack Query
クライアント状態 → Zustand または Jotai のどちらか1つ
ローカル状態 → useState / useReducer
たまの静的なグローバル値 → useContext(テーマ・ロケールなど)ツールをさらに増やす前に、この4つの枠でほとんどのアプリがすっきり整理されます。核心は、状態の種類をまず区別し、種類ごとにふさわしい枠に置くことです。
シリーズを終えて #
6回を貫いたメッセージは、最初と同じです。ツールを覚える前に、状態の性質をまず区別すること。
- 状態にはクライアント状態とサーバー状態があり、両者はよく合うツールが異なります。(#1)
- サーバー状態はTanStack Queryに任せ、キャッシュと再取得を自動化します。(#2)
- 軽量なグローバルクライアント状態には、ZustandやJotaiが合います。(#3、#4)
- Redux Toolkitは、大きなチームと既存のコードベースで今も役割を果たします。(#5)
状態管理は、正解を覚える分野ではなく、状況を読んでふさわしいツールを選ぶ分野です。新しい画面に出会うたびに「この値はどんな種類の状態か」をまず問えば、どのツールを取り出すかは自然に付いてきます。
以上で「React状態管理の深掘り」シリーズを終えます。さらに深いテーマとしては、モダンReact + Next.jsシリーズのServer Components環境で、クライアント状態とサーバー状態がどう分かれるかを続けてご覧になることをおすすめします。