React状態管理の深掘り #6 どのツールをいつ使うか — 決定ガイド

読了 5分

シリーズ最後の記事です。#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つの表にまとめます。

基準ZustandJotai
モデルトップダウン — 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環境で、クライアント状態とサーバー状態がどう分かれるかを続けてご覧になることをおすすめします。

X