Tips & Tricks (更新: 2026/6/7)

React状態管理は「規模」と「種類」で選ぶ:Zustand・Jotai・Reduxの使い分け

useStateで足りるか、Zustandか、Jotaiか、Reduxか。サーバー状態はそもそも別物。規模と種類でライブラリを選ぶ判断軸を、コピペで動くコードと一緒に整理しました。

React状態管理は「規模」と「種類」で選ぶ:Zustand・Jotai・Reduxの使い分け

「状態管理、何使ってますか?」

勉強会でこう聞かれて、僕は一度、見事に地雷を踏みました。「とりあえずReduxですね」と答えたら、その場の半分が微妙な顔をしたんです。残りの半分は「Zustand派です」「いやJotai」と言い出して、軽く宗教論争が始まりました。

あのとき僕が間違っていたのは、ライブラリ名を答えたことそのものでした。正しい問いは「何を管理したいか」だったんです。入力欄の文字と、ログインユーザー情報と、APIから取った商品一覧。これ全部「state」と呼ばれますが、寿命も置き場所も、まるで違う。同じ箱に入れようとするから、毎回もめる。

この記事は、その「どの箱に入れるか」を自分で判断できるようになるための地図です。useStateで足りる話から、Zustand・Jotai・Reduxの分かれ道、そして「サーバー状態はそもそも別ジャンル」という一番大事な線引きまで。各ライブラリの深掘りは個別記事に送るので、まずはここで全体像をつかんでください。

この記事の要点

  • Reactの状態は4種類に分けて考える。ローカルUI / 共有クライアント / 永続化したい設定 / サーバー状態。最初にやるのはライブラリ選びではなく、この仕分け。
  • クライアント状態は規模で選ぶ。1画面ならuseState/useReducer、画面をまたぐならZustand、設定を細かく組み合わせるならJotai、巨大で厳密な履歴管理が要るならRedux Toolkit。
  • サーバー状態(API由来のデータ)は別物。ZustandやReduxに入れず、TanStack Queryのようなデータ取得ライブラリに寄せる。これを混ぜるのが事故の元。
  • 「迷ったらまずReact標準」。3か所以上で更新する/別ページでも使う/リロード後も残す/APIから取る、のどれかに当てはまって初めてライブラリを検討する。

まず、状態を4種類に仕分ける

ライブラリ比較の前に、絶対に外せない作業があります。手元の state を4つに分けることです。ここを飛ばすと、どんなライブラリを選んでも事故ります。

種類具体例寿命置き場所の第一候補
ローカルUI状態入力欄、モーダル開閉、タブ選択コンポーネントが消えたら終わりuseState / useReducer
共有クライアント状態カート、編集中の下書き、ウィザード画面遷移をまたいで生きるZustand / Jotai / Context
永続化したい設定テーマ、表示密度、言語リロードしても残したいpersist / atomWithStorage
サーバー状態商品一覧、注文履歴、プロフィールサーバーが本体。他人や別端末で変わるTanStack Query

この表の一番下、サーバー状態だけ毛色が違うのに気づきますか。ローカルUI状態は「自分のコンポーネントの持ち物」、サーバー状態は「サーバーから借りてるコピー」。持ち物と借り物を同じ引き出しに入れたら、どっちが最新かわからなくなる。後で詳しく書きますが、ここが一番のキモです。

仕分けの目安はシンプルです。次の4つのどれにも当てはまらないなら、ライブラリはいりません。

  • 同じ値を3か所以上で更新する
  • 別ページでも同じ値を使う
  • リロード後も残したい
  • APIから取ってくる

ひとつでも当てはまったら、初めて「どのライブラリか」の話に進みます。逆に言うと、入力欄の文字やモーダルの開閉に外部ライブラリを持ち出すのは、ほぼ過剰設計です。僕も昔、トグルボタンひとつのためにグローバルストアを生やして、後輩に「なんでこれ全部から見えるんですか」と真顔で聞かれました。返す言葉がなかった。

React標準でどこまで戦えるか

意外と、React標準だけで相当いけます。公式のManaging Stateでは、状態を構造化し、必要なら親へ持ち上げ(lifting state up)、複雑になったらreducerとContextを組み合わせる、という順番が示されています。この順番をすっ飛ばして外部ライブラリに飛びつくのが、いちばんよくある回り道です。

判断はこの3段階で足ります。

  1. 1つのコンポーネントで完結するuseState。入力欄、モーダル、開閉トグルはここ。
  2. 複数のアクションがある画面useReducer。「追加」「完了」「削除」のように、起きたことを名前で表現できると、後で読みやすい。
  3. 離れたコンポーネントへ値を渡したい → Context。ただしContextは「更新が頻繁だと再レンダーが重い」という弱点がある。テーマや言語のような滅多に変わらない値に向いています。

Contextを「グローバルストア代わり」に使い始めたら黄信号です。カートのように頻繁に書き換わる値をContextに乗せると、Provider配下が丸ごと再レンダーされて、体感でカクつきます。そこが外部ライブラリへの乗り換えどころです。

クライアント状態は「規模」で分かれる

React標準で苦しくなってきたら、いよいよライブラリです。ここで僕がやっと言語化できた判断軸が「規模」でした。小さい順に並べると、選び方が見えてきます。

ライブラリ向いている規模・性質一言で言うと深掘り
Zustand画面をまたぐ中規模の共有状態一番手軽なグローバルストアZustand設計ガイド
Jotai小さな設定を独立して組み合わせるatom(状態のミニ単位)の集合Jotaiのatom入門
Redux Toolkit大規模・厳密な状態遷移と履歴堅いが冗長、追跡しやすい(本記事で概説)

Zustandは、Reactで「とりあえずグローバルに状態を持ちたい」ときの第一候補です。ストアを1つ作って、どのコンポーネントからもフックで読み書きできる。Contextのような再レンダー地獄が起きにくく、ボイラープレート(お決まりの定型コード)も少ない。カート、サイドバーの状態、ログインユーザー情報あたりが得意分野です。

Jotaiは発想が逆です。大きなストアを1つ持つのではなく、themeAtomdensityAtomのような小さなatomをたくさん作り、必要なものだけ組み合わせる。設定画面のように「独立した小さな状態が大量にある」場面でよく効きます。あるatomから別のatomを計算する「派生atom」が書きやすいのも特徴です。詳しくはJotaiのatom入門にまとめました。

**Redux(Redux Toolkit)**は、この中で一番「堅い」。アクションを定義し、reducerで状態遷移を書き、DevToolsで履歴を1ステップずつ巻き戻せる。コード量は増えますが、大人数で触る大規模アプリや、「いつ・誰が・何を変えたか」を厳密に追いたい業務システムでは、その堅さが武器になります。逆に、トグル数個の小さなアプリにReduxを入れると、定型コードの多さだけが目立ちます。少し前まで「React=Redux」が定番でしたが、今は中小規模ならZustandやJotaiで足りる場面がかなり増えました。

ざっくりした選び方はこうです。手軽さ重視ならZustand、細かい組み立てならJotai、規模と厳密さならRedux Toolkit。迷ったらZustandから入って、不満が出たら他を検討、で大抵うまくいきます。

いちばん大事な線引き:サーバー状態は別ジャンル

ここまでが「クライアント状態」の話でした。で、ここからが本題です。

サーバー状態は、上のどのライブラリにも入れないでください。

商品一覧、注文履歴、プロフィール。これらはサーバーが本体で、ブラウザにあるのは一時的なコピーにすぎません。他のユーザーが在庫を買えば変わるし、別の端末でログインすれば変わる。つまり、自分のアプリが「管理」できる対象じゃないんです。借り物なので。

借り物には、持ち物にはない面倒がついてきます。取得中はどう見せるか、失敗したらどうするか、いつ再取得するか、キャッシュはいつ捨てるか、同じリクエストが重なったらどうまとめるか。これをuseStateuseEffectで全部手書きすると、地獄を見ます。僕は一度やって、loading/error/再取得/キャッシュ破棄のフラグ管理だけでコンポーネントが200行になりました。

その面倒を丸ごと引き受けてくれるのがTanStack Query v5です。キャッシュ、再取得、重複排除、更新後の無効化(invalidate)を標準で持っている。サーバー状態はこれに寄せるのが、いまの実務の定石です。

判別法は簡単。「この値、リロードしたらサーバーから取り直すよな?」と思ったら、それはサーバー状態。ZustandでもReduxでもなく、TanStack Queryの担当です。詳しいキャッシュ設計はTanStack Queryのキャッシュ設計に書きました。

ありがちな勘違いを一つ。「カートはサーバー状態?クライアント状態?」——両方です。画面上で数量をいじる一時的な操作はクライアント状態(Zustand)、確定した注文・在庫・決済結果はサーバー状態(TanStack Query)。同じ「カート」でも層が違う。支払い直前にブラウザのローカル状態だけを信じてはいけません。金額と在庫の最終判断は、必ずサーバーで取り直します。

コピペで動く:選び方を1つの図にした判定関数

文章だと分かった気になるので、判断軸を「動くコード」に落とします。状態の性質を渡すと、推奨ライブラリを返す小さな関数です。設計レビューのときに、チームで「この状態どれ?」を揃える叩き台として使えます。TypeScriptでそのまま動きます。

// 状態の性質を表す入力。レビュー時にこの4つを答えるだけ
type StateProfile = {
  fromApi: boolean;        // APIから取ってくる値か(サーバーが本体か)
  sharedAcrossScreens: boolean; // 別画面でも読み書きするか
  persist: boolean;        // リロード後も残したいか
  manySmallPieces: boolean; // 小さく独立した設定が大量にあるか
  needsStrictHistory: boolean; // 厳密な履歴・大規模で追跡したいか
};

type Recommendation = {
  pick: "useState/useReducer" | "Zustand" | "Jotai" | "Redux Toolkit" | "TanStack Query";
  reason: string;
};

export function pickStateTool(p: StateProfile): Recommendation {
  // 1. サーバー状態は最優先で別扱い。借り物はクライアントストアに入れない
  if (p.fromApi) {
    return {
      pick: "TanStack Query",
      reason: "API由来=サーバーが本体。キャッシュ・再取得・無効化を任せる",
    };
  }

  // 2. 1画面で完結するならライブラリ不要
  if (!p.sharedAcrossScreens && !p.persist) {
    return {
      pick: "useState/useReducer",
      reason: "共有も永続化も不要。React標準で十分",
    };
  }

  // 3. ここからクライアント状態を規模で振り分け
  if (p.needsStrictHistory) {
    return { pick: "Redux Toolkit", reason: "大規模で厳密な状態遷移・履歴が必要" };
  }
  if (p.manySmallPieces) {
    return { pick: "Jotai", reason: "小さく独立した設定をatomで組み合わせる" };
  }
  return {
    pick: "Zustand",
    reason: "画面をまたぐ中規模の共有状態。手軽なグローバルストア",
  };
}

// 使用例:APIから取る商品一覧 → TanStack Query
console.log(
  pickStateTool({
    fromApi: true,
    sharedAcrossScreens: true,
    persist: false,
    manySmallPieces: false,
    needsStrictHistory: false,
  }),
); // → { pick: "TanStack Query", reason: ... }

// 使用例:画面をまたぐカート → Zustand
console.log(
  pickStateTool({
    fromApi: false,
    sharedAcrossScreens: true,
    persist: true,
    manySmallPieces: false,
    needsStrictHistory: false,
  }),
); // → { pick: "Zustand", reason: ... }

この関数のキモは、最初にfromApiを見ているところです。サーバー状態かどうかを真っ先に分岐する。これだけで「APIレスポンスをZustandに突っ込む」事故が構造的に防げます。残りはクライアント状態を規模で振り分けるだけ。判断軸が4つの質問に圧縮されているのが分かると思います。

Claude Codeに移行を頼むときのコツ

この仕分けは、Claude Codeに状態管理のリファクタリングを頼むときにそのまま効きます。「状態管理をいい感じに直して」だけだと、不要なグローバルストアや重複キャッシュが増える。でも、どの状態がどの箱かを先に言語化して渡すと、差分が驚くほど読みやすくなります。

順番が大事です。いきなり実装させず、まず調査だけさせる。

このReactアプリの状態管理を調査してください。まだ編集しないでください。

目的:
- client state と server state を分ける
- React標準で十分な箇所は残す
- Zustand/Jotai/TanStack Queryを入れるなら理由を書く

出力:
- 現在の状態一覧(4分類でラベル付け)
- 重複している状態
- APIレスポンスを手作りキャッシュで抱えている箇所
- 小さく安全に直す順番

調査が返ってきたら、1画面ずつ移行を依頼します。「server stateを先にTanStack Queryへ」「次にカートをZustandへ」のように、層ごとに分けるのがコツ。一括変更はレビューできない巨大diffになります。頼み方そのものを磨きたいならプロンプトの書き方Tips、日々の作業の型はClaude Codeの生産性Tipsが続けて読みやすいはずです。

よくある質問

Q. ZustandとReduxはどっちを選べばいい? A. 中小規模ならZustandで十分なことが多いです。ボイラープレートが少なく、学習も速い。一方、大人数で触る大規模アプリ、厳密な状態遷移の追跡、DevToolsでの履歴巻き戻しが要るならRedux Toolkit。「堅さ」が要るかどうかが分かれ目です。

Q. ZustandとJotaiの違いは? A. 発想が逆です。Zustandは大きなストアを1つ持つ「トップダウン」、Jotaiは小さなatomをたくさん作って組み合わせる「ボトムアップ」。設定画面のように独立した小さな状態が多いならJotai、まとまった共有状態ならZustandが向きます。

Q. サーバー状態もZustandに入れちゃダメ? A. 強くおすすめしません。API由来のデータは再取得・キャッシュ期限・重複排除を自前で書くことになり、すぐ破綻します。TanStack Queryのようなデータ取得ライブラリに寄せると、loading/errorの管理ごと引き受けてくれます。

Q. 全部TanStack Queryじゃダメなの? A. サーバー状態には最高ですが、モーダルの開閉やテーマのようなクライアント状態には向きません。役割が違うので、クライアント状態はuseState/Zustand/Jotai、サーバー状態はTanStack Query、と分けて共存させるのが普通です。

Q. 結局、最初の1個は何を入れればいい? A. 迷ったらまずReact標準(useState/useReducer)で書き始めて、苦しくなった層から外部ライブラリへ。クライアント状態の最初の一手はZustand、APIが絡んだ瞬間にTanStack Query。この2つを押さえれば大半のアプリは回ります。

実際にこの判断軸で整理してみた結果

自分のサイドプロジェクトを、この4分類で棚卸ししてみました。いちばん効いたのは、やっぱりサーバー状態を最初に切り出したことです。「APIから取る値」を全部TanStack Queryへ移しただけで、手書きのloading/error/キャッシュ処理がごっそり消えて、コンポーネントが目に見えて短くなりました。

残ったクライアント状態は、思ったより少なかった。カートとサイドバー開閉くらいはZustand、テーマと表示密度はJotai。Reduxは、今回の規模では出番がありませんでした。あの勉強会で「とりあえずRedux」と答えた過去の僕に、これを見せてやりたい。

ライブラリ選びでもめるのは、たいてい「種類の違うものを同じ箱に入れようとしている」からです。先に4つに仕分ける。それだけで、論争は設計の話に変わります。各ライブラリの実装に踏み込みたくなったら、ZustandJotaiTanStack Queryの個別記事へどうぞ。チームの標準としてCLAUDE.mdやレビュー観点に落とし込みたい場合は、教材・テンプレートも用意しています。

#React #状態管理 #Zustand #Jotai #TanStack Query
無料

無料PDF: Claude Code はじめてのチートシート

まずは無料PDFで基本コマンドと最初の使い方をまとめて確認してください。登録後はそのままテンプレート集や導入相談にも進めます。

スパムは送りません。登録情報は厳重に管理します。

Claude Codeを仕事で使える形にしませんか?

まず無料PDFで基本を固め、繰り返し使う作業はGumroad教材へ、チーム導入や権限設計は導入相談へ進めます。

Masa

この記事を書いた人

Masa

Claude Codeの実務活用、導入設計、収益導線改善を検証しているエンジニア。10言語の技術メディアを運営中。

PR

関連書籍・参考図書

この記事のテーマに関連する書籍を楽天ブックスで探せます。

※ 当サイトは楽天市場のアフィリエイトプログラムに参加しています。上記リンクから商品をご購入いただくと、運営者に紹介料が支払われる場合があります。