
説明
フロントエンドの状態管理ライブラリについて深掘りします。ReduxとかZustand、Jotaiとか、どう使い分ければいいのかを解説します。
登場人物
水野 ゆい
30歳、スタートアップでフロントエンドリードをやっている。React/Next.js/CSSが得意領域で、UIやUXの話になると熱くなる。身近な体験に紐づけて説明するのがうまく、テンポよく話す。バックエンドの話は素直に感心しながら聞くタイプ。堅すぎず、友達と話すような自然な口調。
桐山 しゅん
33歳、SaaS企業でバックエンドエンジニアとして働いている。Go/Node.js/DB設計・インフラが得意領域。論理的だけど穏やかに話し、たまにマニアックな深掘りに走る。フロントの話には新鮮なリアクションを返す。落ち着いたトーンだが親しみやすい。
会話テキスト
- 水野 ゆい
こんにちは、ゆるふわプログラミングラジオへようこそ。
- 桐山 しゅん
どうも、桐山しゅんです。
- 水野 ゆい
水野ゆいです。フロントとバックエンド、それぞれ得意分野の違う2人のエンジニアが、設計パターンやアーキテクチャをゆるく、でも中身はちゃんと話していく番組です。
- 桐山 しゅん
エンジニア中級者の人が「あー、そういうことか」って腹落ちできるような内容を目指してます。よろしくお願いします。
- 水野 ゆい
よろしくお願いします。今回は第2回で、私ゆいがリードです。
- 桐山 しゅん
前回の終わりに「次は状態管理の話にしようかな」って言ってたやつですね。楽しみにしてました。
- 水野 ゆい
準備してきましたよ。フロントエンドの状態管理って、ReduxとかとZustandかJotaiとか、名前はよく出てくるのにどう使い分けるかで迷う人が多くて。
- 桐山 しゅん
バックエンド側から見ると正直ずっと謎で。名前はよく聞くけど何が違うのか全然わかってなかったんですよ。なんか毎年新しいのが出てきてる印象もあって。
- 水野 ゆい
わかる。フロントエンジニアでも「結局どれ入れたら正解?」ってなりがちなんですよね。で、今日はそこを整理したいんですけど、実はライブラリ選びより先にやることがあるんです。
- 桐山 しゅん
先にやること、か。ちょっと気になる。
- 水野 ゆい
そこを飛ばすから毎回迷うんですよ。うちのチームでも最近新機能を追加したとき、「この状態ってどこで管理すればいいんだっけ」ってメンバーが止まってしまって、議論が「ReduxにするかZustandにするか」になってたんですよ。
- 桐山 しゅん
ツール選びに入っちゃってたんですね。
- 水野 ゆい
そう。でも本当に先に考えるべきは「この状態はそもそも何なのか」なんです。状態の種類を整理するところから始めましょう。
- 桐山 しゅん
なるほど。分類が先、ツールが後、ということですね。
- 水野 ゆい
状態って大きく3種類に分けられて、ローカル状態、グローバル状態、サーバ状態の3つです。
- 桐山 しゅん
ローカルとグローバルはなんとなくわかる。サーバ状態って聞き慣れない言葉だけど。
- 水野 ゆい
ローカル状態はコンポーネントの中だけで完結するもの。たとえばモーダルの開閉とか、フォームの入力値とか。「新規作成モーダル」が開いてるか閉じてるかって、他の画面は関係ないじゃないですか。そういうのはわざわざグローバルな場所に置く必要がない。
- 桐山 しゅん
確かに。モーダルが開いてるかどうかって、ページをまたぐ情報じゃないですよね。
- 水野 ゆい
グローバル状態は複数の画面やコンポーネントで共有するもの。ログインユーザーの情報とか、カートの中身とか。
- 桐山 しゅん
ECサイトのカートって典型例ですよね。ヘッダーのバッジと商品ページと決済ページで同じ数字が見えてる。あれって3画面以上が同じデータを参照してる。
- 水野 ゆい
まさにそれ。カートに商品追加したらヘッダーのバッジがすぐ増えて、カートページでも反映されて、決済完了で0に戻る。
- 水野 ゆい
1コンポーネントに閉じ込められないからグローバルに置く必要がある。
- 桐山 しゅん
で、サーバ状態は?
- 水野 ゆい
サーバ状態はサーバから取ってきて、再取得・キャッシュ・同期が必要なもの。商品一覧とか在庫数とか、ユーザー一覧とか。
- 桐山 しゅん
あー、それって管理画面の検索とかフィルタとかページネーションのあたりで絶対問題になりますよね。バックエンド側でも「このエンドポイント、同じパラメータで何度も叩かれてるな」って気になることがあって。
- 水野 ゆい
そうそう。50件ずつ表示して、フィルタが5種類あって、並び替えも2種類あって、1回操作するたびにAPIが再実行される。あれってキャッシュが欲しくなってくるんですよ。
- 桐山 しゅん
キャッシュの話が出てくると、グローバル状態の管理とは別の問題になりそうですね。
- 水野 ゆい
そこが大事で、サーバ状態の主な問題って「再取得・キャッシュ・同期」なんですよ。「データが変わったときに最新に保つ」とか「同じデータを何度もAPIに取りに行かない」とか。
- 水野 ゆい
これをReduxのストアに全部突っ込もうとすると、ローディング状態とかエラーとか再取得のタイミングとか、全部自分で管理しないといけなくなる。
- 桐山 しゅん
バックエンドで言うと、キャッシュの責務をビジネスロジック層に持たせるみたいな話に近い気がして、それは確かにしんどい。
- 水野 ゆい
いい例えかも。サーバ状態は専用の取得・キャッシュの仕組みに任せて、ストアにはUI都合の派生値だけ入れるのが基本方針なんですよ。
- 桐山 しゅん
専用の仕組みって、ライブラリがあるんですか。
- 水野 ゆい
あります。ただ今日はそのライブラリの詳細よりも「サーバ状態をグローバル状態と混同しない」という考え方が大事で。
- 桐山 しゅん
分類が先、ツールが後、か。
- 水野 ゆい
もう一個落とし穴があって、「Reactに標準のコンテキストという仕組みがあるから、グローバル状態は全部そこでいいんじゃない?」って思う人が多いんですよ。
- 桐山 しゅん
標準の仕組みがあるなら追加のライブラリ要らないじゃん、ってなりますよね。依存を減らしたいって気持ちもあるし。
- 水野 ゆい
私も最初そう思ってた。でもコンテキストって値の更新頻度が高かったり参照範囲が広かったりすると、再描画の影響が大きくなりやすいんですよ。変更があるたびに関係ない場所まで再描画される。
- 桐山 しゅん
それはパフォーマンスに響きそう。
- 水野 ゆい
コンテキストは「滅多に変わらない設定系」に向いてる。テーマ設定とか言語設定とか。カートみたいに頻繁に変わるものをコンテキストで管理しようとするとパフォーマンスが落ちやすい。
- 桐山 しゅん
用途が限定されてるんですね。なんでもコンテキストで解決しようとしちゃダメだと。
- 水野 ゆい
まず自分のプロダクトの状態を10個くらい書き出して、ローカル・グローバル・サーバ状態に仕分けしてみるのが最初の一歩としておすすめで。5分あればできるし、それだけで「あ、これグローバルじゃなかった」ってなるものが結構出てくる。
- 桐山 しゅん
意外とローカルで済むものをグローバルに置いてたりしそう。「念のため」でとりあえずグローバルに、みたいな。
- 水野 ゆい
めちゃくちゃあります。バックエンドで言うと、グローバル変数に何でも入れてるみたいな状態ですよね。
- 桐山 しゅん
あー、それは確かに同じ構造だ。
- 水野 ゆい
状態の分類ができると、次にどのライブラリを使うかが自然に絞れてくるんですよ。
- 桐山 しゅん
じゃあライブラリの比較の話に進みましょうか。
- 水野 ゆい
そこ行きましょう。Redux Toolkit、Zustand、Jotai、この3つを設計思想から比べてみます。
- 桐山 しゅん
Reduxって名前は一番よく聞くけど、「古い」とか「重い」とかよく言われてる印象があって。書いたことある人に聞いたら「定型文が多くて疲れた」って言ってた記憶があります。
- 水野 ゆい
それ、昔の書き方の話なんですよ。今はRedux公式がRedux Toolkitというものを推奨していて、昔の書き方よりかなり簡潔になってる。
- 桐山 しゅん
あ、そうなんですか。ツール自体が進化してたんですね。
- 水野 ゆい
Redux Toolkitの設計思想って、単一のストアに状態と更新を集約して、更新の流れを明示しやすくすることなんですよ。
- 水野 ゆい
チーム開発で「誰が見ても変更の流れが追える」のがReduxの強みで。ログが残るし、どの操作でどの状態が変わったか追跡しやすい。
- 桐山 しゅん
バックエンドで言うと、更新履歴が1箇所に集まってるから追いやすい、みたいなイメージですね。デバッグしやすそう。
- 水野 ゆい
ただ、「とりあえずReduxを入れれば設計が良くなる」と思って入れると、状態の分類ができてないまま全部突っ込んで肥大化するんですよ。
- 水野 ゆい
Redux Toolkitには機能単位でスライスという単位で状態と更新をまとめる仕組みがあって、それの命名規則とか構成をチームで合意しておかないと後から読めなくなる。
- 桐山 しゅん
ツールが先で分類が後、になっちゃうとどんなに良いツールでも崩れますね。
- 水野 ゆい
Zustandはもうちょっと軽量な思想で、小さなストアを必要なだけ作って、コンポーネントが必要な断片だけ購読する形なんですよ。機能ごとにストアを分けられるから、シンプルに書ける。
- 桐山 しゅん
単一ストアじゃなくて、複数のストアを作れるんですね。それは柔軟そう。
- 水野 ゆい
ただ「Zustandは小さいから何でもZustandに寄せればいい」ってなると、ストアが増えすぎて依存関係が見えづらくなる。「あの状態どのストアにあったっけ」ってなる。
- 桐山 しゅん
マイクロサービスを増やしすぎてサービス間の依存が把握できなくなる、みたいな話と似てますね。分割は便利だけど、分割しすぎると全体が見えなくなる。
- 水野 ゆい
あ、それすごくいい例えかも。だからZustandを使うなら「どこまでを1つのストアにするか」の境界を決めておく必要があって。機能単位なのかドメイン単位なのかを先に決めて、ストアの一覧をドキュメントに書いておくのが実務での一歩として効く。
- 桐山 しゅん
ドキュメントに書く、か。地味だけど確かに大事ですね。
- 水野 ゆい
Jotaiはまた違う思想で、状態を「アトム」という原子に分割して、依存関係に応じて必要な部分だけが再計算・再描画される。
- 桐山 しゅん
アトムって、めちゃくちゃ細かく分割するイメージ?
- 水野 ゆい
そう。で、アトム同士を組み合わせて派生した状態を作れる。たとえば機能フラグでUIを出し分けるとき、フラグが10個あってユーザー属性が3種類あって表示条件が複雑、みたいな場面だと、アトムの組み合わせで整理しやすい。
- 桐山 しゅん
条件が複雑なときに強そう。ただ、アトムに分ければ勝手にスケールするかというと、そうでもなさそうな気がしてきた。
- 水野 ゆい
そのとおりで、粒度が細かすぎると何がどこにあるか把握が難しくなるし、逆に粗すぎると再描画が減らせないんですよ。
- 桐山 しゅん
データベースのテーブル設計と似た話な気がする。正規化しすぎても逆に使いにくくなる、みたいな。
- 水野 ゆい
めっちゃいい例えで、まさにそれ。Jotaiも「フォーム1つにアトム1個」みたいな粒度のルールを決めて、依存が深くなったらまとめるリファクタの基準を作っておくと破綻しにくい。
- 桐山 しゅん
3つとも思想が全然違うんですね。Reduxが「全部1箇所で追跡」、Zustandが「軽く複数に分割」、Jotaiが「原子から組み上げる」みたいな感じで整理できそう。
- 水野 ゆい
そのまとめ方わかりやすい。あと、更新頻度が高い状態をどこに置くか、って問いも重要で。
- 桐山 しゅん
更新頻度が高いものって、たとえばどういうケースですか。
- 水野 ゆい
リアルタイム通知の未読数みたいな、1分間に何件も増えるような状態をストアに置くとき、セレクタという仕組みが大事になってきて。
- 水野 ゆい
ストアの状態から必要な断片だけを取り出す関数なんですけど、「未読数だけ」を取り出してヘッダーのベルアイコンに渡せば、他の部分は再描画されない。
- 桐山 しゅん
全部再描画しなくていいんですね。それはパフォーマンスに効きそう。データベースのクエリで必要なカラムだけ取ってくる感覚と似てる。
- 水野 ゆい
あ、確かにそれ近い。セレクタの使い方を意識するだけで、更新頻度が高い状態でも快適に動くようになる。
- 桐山 しゅん
じゃあ選び方の基準って、どう考えればいいですか。
- 水野 ゆい
チームの規模とプロダクトの複雑さで変わるんですよ。「誰が見ても変更の流れが追える」を優先するならRedux Toolkit。軽く始めたいならZustand。状態の依存関係が複雑ならJotai。
- 桐山 しゅん
「ReduxよりZustandの方がモダン」みたいな話をよく聞くけど、それって状況によるってことですよね。
- 水野 ゆい
そう。「Reduxは古い」って言われがちだけど、Redux Toolkitは推奨パターンがちゃんと整備されてて、チームの規模が大きくて変更の追跡が必要なら今でも全然有効な選択肢。流行り廃りで判断しちゃいけない。
- 桐山 しゅん
グローバル状態の候補を1つ選んで、「何画面から参照されてるか」と「更新頻度はどれくらいか」をメモするだけで置き場所が見えてくる、ということですね。
- 水野 ゆい
その2つで判断できることが多いです。じゃあ次は実際の運用の話をしたくて。ライブラリを選んだ後のことです。
- 桐山 しゅん
選んで終わりじゃないんですね。
- 水野 ゆい
選んで終わりにすると、後で破綻するんですよ。一番よくあるのが、複数のライブラリが混在して「どこに何があるかわからない」状態になること。
- 桐山 しゅん
あー、プロジェクトが育つにつれて増えていくやつ。新しいメンバーが「このライブラリ使いやすいから」って足していくと、気づいたら3種類共存してる。
- 水野 ゆい
最初はZustandだけだったのに、途中でRedux Toolkitも入って、サーバ状態の管理もどこかに入って、みたいな。
- 桐山 しゅん
それ、どこに何があるか把握するだけで疲弊しそう。
- 水野 ゆい
実はRedux Toolkit、Zustand、Jotaiを併用すること自体は悪くないんですよ。それぞれ得意な場所が違うから。
- 桐山 しゅん
えっ、混在させていいんですか。それは意外。
- 水野 ゆい
「追跡が必要なグローバルな操作はRedux Toolkit、軽いUI状態はZustand」みたいに役割を決めれば成立する。問題は役割を決めずに混在させること。
- 桐山 しゅん
ルールなしに混ぜると収拾がつかなくなる。前回の話でも、フォルダ分けだけして依存の向きを決めなかったら崩れる、って話があったけど、同じ構造ですね。
- 水野 ゆい
まさに。「どのライブラリに何を入れるか」をチームで合意してドキュメントに書いておく。それだけで全然違う。
- 桐山 しゅん
合意してドキュメントに書く、か。設計の意図を残しておくの大事ですよね。
- 水野 ゆい
あと移行の話もしておきたくて。既存プロジェクトにいきなり全部入れ替えるのって現実的じゃないじゃないですか。
- 桐山 しゅん
動いてるプロジェクトをいきなり全部リファクタはリスクが高い。バックエンドでも「全部書き直す」って言い出すと大体炎上する。
- 水野 ゆい
だから新しい機能を追加するときに「この機能から新しいやり方で書く」という局所的な移行が現実的で。前回しゅんが言ってた「局所導入」と同じ考え方ですね。
- 桐山 しゅん
Clean Architectureの話と通じてますね。根っこの考え方が同じ。
- 水野 ゆい
状態管理も設計の話なんですよね。ライブラリの格付けじゃなくて。
- 桐山 しゅん
「分類が先、ツールが後」って話は、バックエンドでも全く同じで。データベースを選ぶ前に、データの性質を整理するのが先、みたいな。
- 水野 ゆい
サーバ状態の扱いももう一回確認しておきたくて。チームで「サーバ状態は専用の取得・キャッシュの仕組みに任せて、ストアにはUI都合の派生値だけ入れる」という方針を合意しておくのが大事で。
- 水野 ゆい
これを決めてないと、誰かが「とりあえずストアに入れとこう」ってやり始めて崩れていく。
- 桐山 しゅん
方針の合意がないと、個人の判断でバラバラになっていく。
- 水野 ゆい
Zustandのストアの境界ルールも同じで、「機能単位か、ドメイン単位か」を決めてREADMEに書いておく。新しいメンバーが入ったときに「このプロジェクトの状態管理はこういうルールです」って一枚で説明できる状態にしておく。
- 桐山 しゅん
オンボーディングのコストが下がりますよね。それ、チームが大きくなるほど効いてくる。
- 水野 ゆい
今日の話、しゅんはバックエンド側から聞いてどうでしたか。
- 桐山 しゅん
正直、状態管理ってフロント独自の問題だと思ってたんですよ。でも「どこに何を置くか」「更新の責務をどう分離するか」って、バックエンドのデータ設計と同じ問いだなと思って。マイクロサービスの分割の話とか、キャッシュの責務の話とか、全部つながってる気がした。
- 水野 ゆい
そうなんですよ。前回の依存の整理と今回の状態の分類って、根っこの考え方が似てる。
- 桐山 しゅん
フロントとバックエンドで使う言葉は違うけど、解こうとしてる問題は同じなんですね。
- 水野 ゆい
まとめると、状態管理って「ライブラリの格付け」じゃなくて、まず状態の種類で仕分けして、次に設計思想の違いで選んで、最後に運用ルールで破綻を防ぐ、この順番で考えるのがコツなんですよね。
- 桐山 しゅん
順番が大事なんですね。逆から入ると迷子になる。
- 水野 ゆい
今日の持ち帰りは「状態を10個書き出して分類する」だけでいいです。ローカルかグローバルかサーバ状態か。それだけで「Reduxが必要か、Zustandで十分か、Jotaiが効くか」がかなりクリアになる。
- 桐山 しゅん
10個書き出すだけなら今日帰ってからでもできそう。5分でできるって言ってたし。
- 水野 ゆい
ぜひやってみてほしいです。
- 桐山 しゅん
状態管理って難しそうで避けてたけど、整理の仕方がわかると全然違いますね。聞いてよかった。
- 水野 ゆい
今日の話、聞いてくれているみなさんはどうでしたか。「うちのプロジェクトはこうなってる」とか「試してみた」とか、ぜひコメントや感想を送ってほしいです。
- 桐山 しゅん
概要欄にお便りフォームのリンクを載せているので、ぜひ使ってください。テーマのリクエストも大歓迎です。
- 水野 ゆい
番組が気に入ったら、フォローと高評価もよろしくお願いします。続けるモチベーションになります。
- 桐山 しゅん
次回はどんなテーマにしますか。
- 水野 ゆい
まだ悩んでるんですけど、テスト設計の話もしてみたいなと思ってて。どこまでテストするか、何をテストするか、みたいな。
- 桐山 しゅん
それはフロントもバックエンドも共通の悩みだから、両方の視点で話せそう。楽しみにしてます。
- 水野 ゆい
では、また次回お会いしましょう。
- 桐山 しゅん
またねー、バイバイだよ。
- 水野 ゆい
またねー。
こちらのエピソードもおすすめ

ゆるふわプログラミングラジオ
第1回はバックエンド担当のしゅんがリード。Clean Architectureの考え方を「なぜ層を分けるのか」「依存の方向ってどういうこと?」という切り口で解説。ゆいがフロント視点から「コンポーネント設計と似てる?違う?」と切り込みます。
