🦫
🦫 Zustand入門してみた
Zustand(ズスタンド)とは
Reactアプリで「状態(state)」をシンプルに・安全に・直感的に扱うためのライブラリ。
たとえば、
「グローバルに count を共有して、どのコンポーネントからでも増減したい」
というときに、useState だけではスコープが狭く、Redux は重すぎる。
→ そんなときに Zustand。ありがたい。
⚙️ 基本のイメージ
Store(倉庫)
├─ Counter.tsx …… useCounterStore() を通じて Store を参照
├─ Header.tsx …… useCounterStore() を通じて Store を参照
└─ Footer.tsx …… useCounterStore() を通じて Store を参照
- Storeはアプリ全体で1つの状態の倉庫
- 各コンポーネントは「必要な変数・関数だけ」取り出して使う
- Reactの再レンダーは「その変数を使っている部分だけ」に限定される
⚖️ Zustandのメリット・デメリット
✅ メリット(Pros)
| 項目 | 内容 | 備考 |
|---|---|---|
| 🧩 シンプル |
create()でstoreを1行で作れる |
Reduxのようなreducerやactionが不要 |
| ⚡️ 軽量・高速 | 選択的subscribeで無駄な再レンダーなし | RecoilやContext APIより速いケース多い |
| 🧠 Hooksベース |
useStore()で自然に状態取得できる |
React Hooksの感覚そのまま |
| 🔁 再利用性 | Storeを複数作って分離できる | 状態管理のスコープを細かく切れる |
| 💾 永続化 | localStorage対応(persistミドルウェア) |
ページ更新してもデータ保持可能 |
| 🧮 型安全(TS) | TypeScriptと相性抜群 | 型推論も強力 |
| 🔍 DevTools連携 | Redux DevToolsが使える | 状態履歴の確認も可能 |
❌ デメリット(Cons)
| 項目 | 内容 | 対応策 |
|---|---|---|
| ⚙️ 構造が自由すぎる | 設計パターンを決めておかないとカオスに | store命名・責務分離をルール化する |
| 🧱 Contextより低レベル | ReactのContext APIのような「自動プロバイダ」がない | storeを自分でimportする必要あり |
| 🧩 SSR対応に注意 | Hydrationミスに注意(Next.jsなど) |
useEffect内でクライアント初期化する |
| 🔄 大規模連携が弱い | GraphQL・API層との連携を自作する必要 | React QueryやSWRと併用が一般的 |
⚔️ 他ライブラリとの比較
| ライブラリ | 特徴 | 適する規模 | 状態共有の粒度 | 備考 |
|---|---|---|---|---|
| Zustand | 軽量・直感的。Hooksベース。 | 小〜中規模 | 高精度(選択的) | UI・アニメ・PixiJSにも◎ |
| Redux Toolkit | 組織的・拡張性◎ | 中〜大規模 | 中精度 | 明確なフロー(action→reducer) |
| Recoil | React製。依存関係の追跡が強い | 中規模 | 高精度 | 部分的state管理に最適 |
| Jotai | 最小単位(atom)で状態管理 | 小〜中規模 | 高精度 | Functional & 直感的 |
| Context API | React標準機能 | 超小規模 | 低精度 | 再レンダーが広がりやすい |
🧭 簡単にまとめると…
| シチュエーション | 最適な選択 |
|---|---|
| シンプルにグローバル変数を共有したい | ✅ Zustand |
| 大規模アプリでチーム開発 | ⚙️ Redux Toolkit |
| 複雑な依存関係・派生stateが多い | 🧠 Recoil |
| 個人開発・軽いUI管理 | 🎯 Jotai or Zustand |
🔄 状態の流れ(イメージで理解)
Zustandでは「変数 → 更新 → 反映」の流れが超シンプルです。
🧩 例:カウンターアプリの処理フロー
[1] Store作成
└ count = 0
increase() { count++ }
decrease() { count-- }
[2] UIでボタン押下
└ onClick → store.increase()
[3] storeのcountが更新
└ set((state) => ({ count: state.count + 1 }))
[4] Zustandが再レンダーを検知
└ countを使っているコンポーネントだけ更新
[5] UI反映
└ <h1>Count: 1</h1>
📈 Reduxとの違い:
Reduxは「action → reducer → dispatch → state更新 → subscribe」と5段階だが、
Zustandは「set → state更新 → 自動通知」のわずか3ステップ。
処理の流れ
[1] UIでshape選択 → store.setShape("square")
[2] 状態が更新され、Canvas描画コンポーネントが自動再レンダー
[3] 「開始」ボタン押下 → store.toggleSimulation()
[4] 状態が true に変わり、PixiJSが開始される
[5] 停止ボタン → toggleSimulation() → false に戻る
➡️ PixiJSやThree.jsのようなリアルタイム描画とも親和性が高い。
状態をFluxライクに扱えるのがZustandの真骨頂。
🧾 まとめ(Zustandが選ばれる理由)
| 観点 | 理由 |
|---|---|
| 🚀 軽量・高速 | Contextより10〜30倍速いケースも |
| 🧠 学習コスト | 5分で導入可能。Hooks感覚でOK |
| 🧩 柔軟性 | storeを複数に分けられる(UI用・API用など) |
| 💾 永続化 | localStorageで状態保持可能 |
| 🔧 DevTools | Redux DevToolsでデバッグ容易 |
| 🧍♂️ 個人開発〜小チーム開発 | 構造が直感的でコードが短い |
Discussion