C
React/状態管理(応用)/Lesson 17

Zustandグローバル状態管理

30分·theory
TypeScript

Zustandグローバル状態管理

💡 なぜ学ぶべきなのか?

🎯 Reduxよりバンドルサイズが95%小さいにもかかわらず、同等の機能を提供します。
💼 ボイラープレートコードなしに、直感的にグローバル状態を管理できます。
TypeScriptのサポートが完全で、型安全性に優れています。
🔗 2024年現在、スタートアップから大手企業まで、Zustandへの移行が進んでいます。
🏢 실무에서는
実務では、ユーザーのログイン情報・テーマ設定・ショッピングカートなどをZustandで管理します。Reduxのような複雑なアクション/リデューサーの記述は不要で、シンプルな関数で状態を変更でき、どのコンポーネントからも必要な状態だけを購読して利用できます。

概念

2025年現在、React状態管理の二大勢力であるZustandとRecoilの実務における選択基準を学びます。バンドルサイズ、学習曲線、型安全性など、実際のプロジェクトで考慮すべきすべての要素を扱います。

なぜ重要なのか?

大規模サービスでReduxの代わりにZustandを採用し、バンドルサイズを30%削減して開発生産性を2倍向上させた事例が増えています。特にMetaが開発したRecoilとともに次世代状態管理ソリューションとして注目されており、2025年のフロントエンド面接では必須の質問として登場しています。

コアコンセプト

Zustandはドイツ語で「状態」を意味し、非常に軽量な状態管理ライブラリです。小さな引き出しのように、必要な状態だけをシンプルに保管します。Recoilはフェイスブックが作ったatomベースの状態管理で、レゴブロックのように小さな状態単位を組み合わせて複雑な状態を構築します。

キーポイント

  • Zustand: 2.9KB vs Recoil: 79KB — バンドルサイズではZustandの圧勝
  • Zustandはボイラープレート不要、Recoilはatom/selectorパターンで細かい制御が可能
  • 2025年時点でZustandがより安定しており、Recoilは依然として実験的な段階
💻 Zustand基本実装 — 2025推奨パターン
import { create } from 'zustand'
import { persist } from 'zustand/middleware'

// 2025年最新パターン: immerとpersistの組み合わせ
interface UserStore {
  user: User | null
  isLoading: boolean
  login: (credentials: LoginCredentials) => Promise<void>
  logout: () => void
}

export const useUserStore = create<UserStore>()(persist(
  (set, get) => ({
    user: null,
    isLoading: false,
    
    login: async (credentials) => {
      set({ isLoading: true })
      try {
        const user = await authAPI.login(credentials)
        set({ user, isLoading: false })
      } catch (error) {
        set({ isLoading: false })
        throw error
      }
    },
    
    logout: () => {
      set({ user: null })
      authAPI.logout()
    }
  }),
  {
    name: 'user-storage',
    partialize: (state) => ({ user: state.user }) // 必要なものだけpersist
  }
))
💻 Recoil基本実装 — アトミックパターン
import { atom, selector, useRecoilState, useRecoilValue } from 'recoil'

// Atom定義
export const userAtom = atom<User | null>({
  key: 'userAtom',
  default: null,
})

export const isLoadingAtom = atom<boolean>({
  key: 'isLoadingAtom',
  default: false,
})

// Selectorで派生状態を定義
export const userStatusSelector = selector({
  key: 'userStatusSelector',
  get: ({ get }) => {
    const user = get(userAtom)
    const isLoading = get(isLoadingAtom)
    
    if (isLoading) return 'loading'
    if (user) return 'authenticated'
    return 'guest'
  },
})

// コンポーネントで使用
function UserProfile() {
  const [user, setUser] = useRecoilState(userAtom)
  const userStatus = useRecoilValue(userStatusSelector)
  
  return (
    <div>
      <p>状態: {userStatus}</p>
      {user && <p>ようこそ、{user.name}様!</p>}
    </div>
  )
}

💡 ⚠️ よくある間違い

  • Zustandで不必要に複数のストアに分割し、propsドリリング問題を再発生させるケース
  • Recoilでatom keyを重複して使用し、予期しない状態の共有が発生するケース
  • バンドルサイズを考慮せずに小規模プロジェクトにRecoilを導入し、オーバーエンジニアリングになるケース

💡 🎯 面接対策

Q: ZustandとRecoilのどちらを選びますか?その理由は?
Q: Reduxと比較したZustandの利点は何ですか?
Q: Recoilのatomとselectorパターンを説明してください

ヒント: プロジェクトの規模とチームの状況を基準に選択 — 小〜中規模・高速開発はZustand、大規模・複雑な状態依存性はRecoil。バンドルサイズ、学習曲線、型安全性、コミュニティサポートを総合的に言及し、実務経験の事例を含める

⚛️ Reactパターン — Zustandグローバル状態管理

Zustandグローバル状態管理をReactでどのように使うか、コードとともにステップごとに学びましょう。
1 🧩 1. Zustandグローバル状態管理を使うシナリオ
この機能が必要な場面。
2 💻 2. コードを書く
Zustandグローバル状態管理の基本的な使い方。
3 🎨 3. レンダリング結果
ユーザーが見る画面。
4 💡 4. 実践的なヒント
よくある落とし穴とベストプラクティス。

🎮 Zustandグローバル状態管理 — ステップごとの理解

各ステップをクリックして内容を読み、✓ 理解しました ボタンで進捗を確認してください。
🖥️ 実行結果 — レンダリングされたReactコンポーネント
✏️ React 코드 수정하기 (클릭해서 열기)
⚛️ React 18 + Babel Standalone — まず結果を確認し、エディタで自由に編集できます。

確認クイズ

Zustandの利点として正しいものはどれですか?
💡 Zustandはストアの作成・購読・更新を最小限のコードで記述できます。Reduxのような複雑なアクション/リデューサー/ミドルウェアは不要で、`const useStore = create(set => ...)` から始めるだけです。
先に読むとよい概念: React Query (TanStack Query)
Zustand — 今日のトップティアグローバルステート - React