
state-management
by xmark168
SKILL.md
name: state-management description: Manage client-side state with Zustand. Use when building shopping cart, user preferences, theme toggle, UI state, or any cross-component shared state with localStorage persist.
This skill guides implementation of client-side state management using Zustand.
The user needs to share state between components, persist data to localStorage, or manage complex UI state like shopping carts or theme preferences.
Before You Start
Zustand is the preferred state management library:
- File location:
lib/stores/<name>-store.ts - Function: Use
createfrom zustand (NOT createStore) - Components: Must have
'use client'to use stores
CRITICAL: Keep stores small and focused. Create one store per domain (cart, theme, user preferences) rather than one large store.
Basic Store
// lib/stores/cart-store.ts
import { create } from 'zustand';
interface CartItem {
id: string;
name: string;
price: number;
quantity: number;
}
interface CartStore {
items: CartItem[];
addItem: (item: CartItem) => void;
removeItem: (id: string) => void;
updateQuantity: (id: string, quantity: number) => void;
clearCart: () => void;
total: () => number;
}
export const useCartStore = create<CartStore>((set, get) => ({
items: [],
addItem: (item) =>
set((state) => {
const existing = state.items.find((i) => i.id === item.id);
if (existing) {
return {
items: state.items.map((i) =>
i.id === item.id ? { ...i, quantity: i.quantity + item.quantity } : i
),
};
}
return { items: [...state.items, item] };
}),
removeItem: (id) =>
set((state) => ({
items: state.items.filter((i) => i.id !== id),
})),
updateQuantity: (id, quantity) =>
set((state) => ({
items: state.items.map((i) => (i.id === id ? { ...i, quantity } : i)),
})),
clearCart: () => set({ items: [] }),
total: () => get().items.reduce((sum, i) => sum + i.price * i.quantity, 0),
}));
Store with localStorage Persistence
Use the persist middleware for data that should survive page refresh:
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
interface ThemeStore {
theme: 'light' | 'dark';
setTheme: (theme: 'light' | 'dark') => void;
toggleTheme: () => void;
}
export const useThemeStore = create<ThemeStore>()(
persist(
(set) => ({
theme: 'light',
setTheme: (theme) => set({ theme }),
toggleTheme: () =>
set((state) => ({ theme: state.theme === 'light' ? 'dark' : 'light' })),
}),
{ name: 'theme-storage' } // localStorage key
)
);
Using Stores in Components
Always use selectors to prevent unnecessary re-renders:
'use client';
import { useCartStore } from '@/lib/stores/cart-store';
export function CartButton() {
// Selector - only re-renders when items.length changes
const count = useCartStore((state) => state.items.length);
return <button>Cart ({count})</button>;
}
export function CartTotal() {
const total = useCartStore((state) => state.total());
return <span>${total.toFixed(2)}</span>;
}
Multiple Selectors with useShallow
When selecting multiple values, use useShallow to prevent re-renders if values haven't changed:
'use client';
import { useShallow } from 'zustand/react/shallow';
import { useCartStore } from '@/lib/stores/cart-store';
export function CartSummary() {
const { items, total } = useCartStore(
useShallow((state) => ({
items: state.items,
total: state.total(),
}))
);
return (
<div>
<p>{items.length} items</p>
<p>Total: ${total.toFixed(2)}</p>
</div>
);
}
Accessing Actions
Actions don't cause re-renders, so you can select them directly:
'use client';
import { useCartStore } from '@/lib/stores/cart-store';
export function AddToCartButton({ product }) {
const addItem = useCartStore((state) => state.addItem);
return (
<button onClick={() => addItem({ ...product, quantity: 1 })}>
Add to Cart
</button>
);
}
NEVER:
- Use
createStore(usecreateinstead) - Select the entire store without selectors (causes unnecessary re-renders)
- Forget
'use client'in components using stores - Create one giant store (split by domain)
- Access stores in Server Components
IMPORTANT: Zustand stores are client-side only. For server-side state, use Server Components with direct database queries.
スコア
総合スコア
リポジトリの品質指標に基づく評価
SKILL.mdファイルが含まれている
ライセンスが設定されている
100文字以上の説明がある
GitHub Stars 100以上
3ヶ月以内に更新がある
10回以上フォークされている
オープンIssueが50未満
プログラミング言語が設定されている
1つ以上のタグが設定されている
レビュー
レビュー機能は近日公開予定です