第八章:useStateでは限界を超えたとき - Zustand状態管理マスター

あなたの管理システムが1、2ページしかない場合、第1章で学んだuseStateで十分です。 しかし、システムが大きくなるにつれて、大きな問題に直面するかもしれません:

ダッシュボードページで打刻APIを呼び出し、punchRecordsという配列を取得したとします。 チャートコンポーネントはこの配列を使ってグラフを描画する必要があり、データテーブルコンポーネントはリスト表示に使い、サイドバーは今日の遅刻者数を計算するために必要です。

useStateだけを使う場合、このpunchRecordsを最上位の親コンポーネントから子コンポーネントへ、propsを介してリレーのように次々に渡さなければなりません。 この現象は業界で**「Prop Drilling(プロパティドリリング)」**と呼ばれています。これはコードを複雑にし、途中のどこかで渡し忘れるとシステム全体が壊れてしまいます!

この問題を解決するためには、「グローバル状態管理(Global State Management)」が必要です。

🎯 本章の目標

  1. グローバル状態管理の概念を理解する:Store(ストア)とは何か?
  2. Reactコミュニティで最も推奨され、軽量な状態管理ライブラリZustandを学ぶ。
  3. ログインユーザー情報を管理するグローバルなuserStoreを作成する。
  4. 全く関係のない2つのコンポーネント間で、同じグローバルデータを読み書きする方法を学ぶ。

🐻 なぜ老舗のReduxではなくZustandを選ぶのか?

5年前のReactチュートリアルを調べると、必ずReduxを使うように教えています。 Reduxは強力ですが、小さな変数を保存するだけでも数十行のボイラープレートコードが必要で、ReducerDispatchActionといった人間味のない専門用語が溢れています。

Zustand(ドイツ語で「状態」の意味)は可愛い熊です。その設計理念は「シンプルであるほど良い」です。 Zustandはクラウド上に「共有の大倉庫(Store)」を作成し、どのコンポーネントも対応する鍵(Hook)さえ持っていれば、直接倉庫からデータを取得したり変更したりできます。親コンポーネントを介す��必要はまったくありません!


📦 ステップ1:AIに倉庫を作成してもらう

🔥【Vibe Prompt実戦呪文】 React(Vite)でZustandをグローバル状態管理として使用したい。 1. npmインストールコマンドを教えてください。 2. src/store/にuserStore.tsを作成してください。 3. このストアにはuserInfo(name、role、avatarフィールドを含むオブジェクト、デフォルトはnull)を含めてください。 4. 状態を更新する2つの関数を提供してください:setUserInfo(ログイン時にデータを書き込む)とclearUser(ログアウト時にデータをクリアする)。 5. TypeScriptの型定義を追加して、エディターで自動補完が効くようにしてください。

AIはターミナルを開いてインストールするように教えてくれます:

npm install zustand

そしてこの非常にエレガントなストア設定ファイルを生成します:

// 📂 src/store/userStore.ts
import { create } from 'zustand';

// 1. 倉庫の中身を定義(TypeScript型定義)
interface UserInfo {
  name: string;
  role: string;
  avatar?: string;
}

interface UserState {
  userInfo: UserInfo | null;
  setUserInfo: (user: UserInfo) => void;
  clearUser: () => void;
}

// 2. この大倉庫を作成して公開
export const useUserStore = create<UserState>((set) => ({
  userInfo: null, // ウェブページを開いた時点では誰もログインしていない
  
  // データを書き込む魔法:set()関数を呼び出して倉庫を更新
  setUserInfo: (user) => set({ userInfo: user }),
  
  // データをクリアする魔法
  clearUser: () => set({ userInfo: null }),
}));

🚀 ステップ2:どこからでも「自由にアクセス」

今、この倉庫はあなたのウェブサイトの上空に浮かんでいます。サイトのどこからでもデータを取得できます!

シナリオA:ナビゲーションバー(Navbar.tsx)で名前を取得

import { useUserStore } from '../store/userStore';

export default function Navbar() {
  // 先ほど作成したHookを直接呼び出し、「userInfoだけ欲しい」と伝える
  const userInfo = useUserStore((state) => state.userInfo);
  
  return (
    <nav className="bg-white p-4 flex justify-between shadow-sm">
      <div className="font-bold">打刻システム管理画面</div>
      {/* ログイン状態に応じて異なるテキストを表示 */}
      <div className="text-blue-600">
        こんにちは、{userInfo ? userInfo.name : 'ゲスト'}さん
      </div>
    </nav>
  );
}

シナリオB:ログインページ(Login.tsx)でデータを書き込む

import { useUserStore } from '../store/userStore';

export default function Login() {
  // 今回は倉庫に「setUserInfoという書き込み関数が欲しい」と伝える
  const setUserInfo = useUserStore((state) => state.setUserInfo);

  const handleFakeLogin = () => {
    // APIログインが成功したと仮定し、直接グローバル倉庫にデータを保存!
    setUserInfo({ name: '王社長', role: 'admin' });
    alert('ログイン成功!上記のNavbarの名前が変わったか確認してください');
  };

  return (
    <button onClick={handleFakeLogin} className="bg-blue-500 text-white p-2 rounded">
      模擬ログイン
    </button>
  );
}

NavbarLoginは完全に独立した2つのコンポーネントですが、LoginsetUserInfoを実行すると、Navbarの表示が瞬時に「王社長」に変わります!これがグローバル状態の強力な威力です。

💼 [ビジネス応用シナリオ] Zustandに何を入れるべきか?

Zustandを学んだ初心者がよく犯す間違いは、すべての変数(入力中のテキストやポップアップの開閉状態まで)をZustandに詰め込むことです。これによりメモリが肥大化し、保守が非常に難しくなります。

重要なビジネス原則を覚えておいてください:「複数のページやコンポーネントで共有する必要があるデータだけをZustandに入れる」

  • ✅ 適しているもの:ログインユーザー情報、サイト全体のダーク/ライトモード、ショッピングカート内の商品数
  • ❌ 適していないもの:特定のページ専用のデータテーブルの検索キーワード、ボタンをクリックしたときの確認ダイアログ表示(これはuseStateで十分)

✅ 本章のまとめ

Zustandをマスターすれば、Reactプロジェクトのアーキテクチャ能力は真の「初心者レベル」を脱します。 今後どれほど複雑なeコマースシステムを作る場合でも、ページを跨ぐデータをZustandに抽出すれば、コンポーネントツリーは驚くほどクリーンで整理された状態を保てます。これは大規模な企業プロジェクトを開発する際に、崩壊しないための最強の防御策となるでしょう!

完全なチュートリアルをロック解除

このチャプターは有料コンテンツです。プロジェクトに参加して、10以上の神レベルのPromptや実際のソースコード例を含む、5000字以上の深い分析をロック解除してください!