zustand について
プロジェクトでZustandを使う開発者向けの状態管理ガイドです。ドメイン別ストア、型付きセレクター、ミドルウェアを扱い、共有クライアント状態とサーバー状態を区別します。
主な機能
- 01ドメイン別ストア設計
- 02TypeScriptによる型安全性
- 03大規模ストア向けスライスパターン
- 04購読を絞るセレクターフック
- 05永続化とImmerミドルウェア
活用シーン
- 01新機能のストア設計
- 02大規模ストアのスライス分割
- 03必要な状態だけを購読
- 04永続化する状態の選定
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ pedronauck/ skills --skill zustandインストール後、Claude Code で/zustandを入力して読み込まれたか確認します。
同梱ファイルreferences/patterns.md
詳細
Zustand の状態管理ガイド
このスキルでは、このプロジェクトで Zustand を使用するためのガイドライン、パターン、ベストプラクティスを紹介します。
クイックスタート
ストアの詳細なパターン、ミドルウェアの使用方法、包括的な例については、references/patterns.md を参照してください。
基本理念
- 共有クライアント状態のみ:Zustand は共有クライアント状態に使用し、サーバー状態には使用しないでください(サーバー状態には TanStack Query を使用してください)。
- ドメイン固有のストア:ストアは特定のドメインに特化させてください。
- 型安全性:TypeScript を活用して、ストアに完全な型を付けてください。
- シンプルさ:複雑な抽象化よりもシンプルさを優先してください。
ストアの構成
必須パターン
- ドメインごとに個別のストアを作成する
- 大規模なストアにはスライスパターンを使用する
- ストアは、それを使用する機能の近くに配置する
- DX とパフォーマンスを向上させるため、型付きセレクターフックをエクスポートする
推奨ミドルウェア構成
本番用ストアでは、次のミドルウェアを組み合わせて使用してください。
import { create } from "zustand";
import { devtools, persist } from "zustand/middleware";
import { immer } from "zustand/middleware/immer";
export const useExampleStore = create<ExampleState>()(
devtools(
persist(
immer((set, get) => ({
// state and actions
})),
{ name: "example-storage" }
),
{ name: "example-store" }
)
);
ストア構造のテンプレート
interface StoreState {
// State properties
data: DataType | null;
isLoading: boolean;
// Group actions together
actions: {
fetchData: () => Promise<void>;
updateData: (updates: Partial<DataType>) => void;
reset: () => void;
};
}
セレクターフックのパターン
パフォーマンスを最適化するため、必ずセレクターフックを作成してください。
// Bad - subscribes to entire store
const { user, isLoading } = useAuthStore();
// Good - subscribes only to specific slices
export const useUser = () => useAuthStore((state) => state.user);
export const useIsLoading = () => useAuthStore((state) => state.isLoading);
export const useAuthActions = () => useAuthStore((state) => state.actions);
ベストプラクティス
- ストアは特定のドメインに特化させる
- TypeScript を使用して、完全な型安全性を確保する
- ミドルウェアを活用して、一般的なパターンに対応する(devtools、persist、immer)
- パフォーマンス向上のためにセレクターフックを作成する
- 複雑なネスト状態の更新には immer を使用する
- 必要な状態のみを永続化する —
partializeオプションを使用する - ストアを十分にテストする
- ローディング状態とエラー状態を使って、非同期処理を適切に扱う
- 適切な場合は楽観的更新を実装する
- ストアの構造とアクションを文書化する
よくあるタスク
新しいストアの作成
- 型付きアクションを含む状態インターフェースを定義する
- 適切なミドルウェアを使ってストアを作成する
- 状態の各スライスに対応するセレクターフックをエクスポートする
- 機能のバレルエクスポートに追加する
状態の永続化
必要なデータのみを永続化するには、partialize とともに persist ミドルウェアを使用してください。
persist(
(set) => ({ /* ... */ }),
{
name: "store-key",
partialize: (state) => ({
// Only persist these fields
user: state.user,
preferences: state.preferences,
}),
}
);
更新への Immer の使用
Immer を使用すると、変更可能な形式で更新を記述しながら、不変の状態を生成できます。
immer((set) => ({
updateNested: (id, value) => {
set((state) => {
const item = state.items.find((i) => i.id === id);
if (item) {
item.value = value; // Mutable style, but produces immutable state
}
});
},
}));
検証チェックリスト
Zustand に関するタスクを完了する前に、次の項目を確認してください。
- ストアがドメイン固有で、特定の領域に特化している
- TypeScript インターフェースが適切に定義されている
- ミドルウェアが正しい順序で適用されている(devtools > persist > immer)
- パフォーマンス向上のためのセレクターフックが作成されている
- アクションが
actionsオブジェクトにまとめられている - 必要な状態のみが永続化されている
- 型チェック(
pnpm run typecheck)とテスト(pnpm run test)を実行する
詳細なルール、例、アンチパターンについては、references/patterns.md を参照してください。
コメント
Markdown 対応