Skip to main content

state-management

State management patterns. Local state, global state, server state. Zustand, Redux, Jotai, TanStack Query, React Context.

소스 정보

저장소
skeletorflet/opencode-kit
최근 소스 활동
2026년 4월 3일 15:32
감지된 SKILL.md 언어
영어
스타
9
포크
1

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
state-management
description
State management patterns. Local state, global state, server state. Zustand, Redux, Jotai, TanStack Query, React Context.
# State Management > Use the right tool for the right type of state. --- ## 1. State Classification | Type | Description | Tool | |------|-------------|------| | **Local UI** | Component-only (open/closed, hover) | useState | | **Shared UI** | Cross-component, no server | Zustand / Jotai / Context | | **Server/Async** | Data from API, loading/error states | TanStack Query / SWR | | **URL** | Navigation state, shareable | URLSearchParams / router | | **Form** | Input values, validation | React Hook Form | | **Persistent** | Survives page reload | localStorage + store | --- ## 2. Decision Tree ``` Is it server data? └─ YES → TanStack Query / SWR Is it only used in one component? └─ YES → useState / useReducer Is it form data? └─ YES → React Hook Form Is it global app state (auth, theme, cart)? └─ YES → Zustand / Jotai Does it need time-travel / devtools? └─ YES → Redux Toolkit Is it just prop drilling 2-3 levels? └─ Use props, not context ``` --- ## 3. TanStack Query (Server State) ```tsx // Fetch const { data, isLoading, error } = useQuery({ queryKey: ["users", filters], queryFn: () => fetchUsers(filters), staleTime: 5 * 60 * 1000, // 5 min }); // Mutate const mutation = useMutation({ mutationFn: createUser, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["users"] }); }, }); // Optimistic update const mutation = useMutation({ mutationFn: updateTodo, onMutate: async (newTodo) => { await queryClient.cancelQueries({ queryKey: ["todos"] }); const prev = queryClient.getQueryData(["todos"]); queryClient.setQueryData(["todos"], old => old.map(t => t.id === newTodo.id ? newTodo : t) ); return { prev }; }, onError: (err, newTodo, context) => { queryClient.setQueryData(["todos"], context.prev); }, }); ``` --- ## 4. Zustand (Global Client State) ```tsx import { create } from "zustand"; import { persist } from "zustand/middleware"; interface CartStore { items: CartItem[]; addItem: (item: CartItem) => void; removeItem: (id: string) => void; clear: () => void; } const useCartStore = create<CartStore>()( persist( (set) => ({ items: [], addItem: (item) => set((state) => ({ items: [...state.items, item] })), removeItem: (id) => set((state) => ({ items: state.items.filter(i => i.id !== id) })), clear: () => set({ items: [] }), }), { name: "cart-storage" } ) ); // Usage: const { items, addItem } = useCartStore(); // Selector (prevents re-render): const items = useCartStore(s => s.items); ``` --- ## 5. Jotai (Atomic State) ```tsx import { atom, useAtom, useAtomValue } from "jotai"; const countAtom = atom(0); const doubleAtom = atom((get) => get(countAtom) * 2); // Async atom const userAtom = atom(async () => { const res = await fetch("/api/user"); return res.json(); }); function Counter() { const [count, setCount] = useAtom(countAtom); const double = useAtomValue(doubleAtom); return <button onClick={() => setCount(c => c + 1)}>{count} ({double})</button>; } ``` --- ## 6. Context (Simple Sharing) ```tsx // Use for: theme, locale, auth user (read-mostly) // AVOID for: frequently updated state (causes all consumers to re-render) const ThemeContext = createContext<Theme>("light"); export function ThemeProvider({ children }: { children: React.ReactNode }) { const [theme, setTheme] = useState<Theme>("light"); return ( <ThemeContext.Provider value={{ theme, setTheme }}> {children} </ThemeContext.Provider> ); } ``` --- ## 7. URL State ```tsx // For: search, filters, pagination, tabs // Survives refresh + shareable const [searchParams, setSearchParams] = useSearchParams(); const page = Number(searchParams.get("page") ?? "1"); const query = searchParams.get("q") ?? ""; const updateFilter = (key: string, value: string) => { setSearchParams(prev => { prev.set(key, value); return prev; }); }; ``` --- ## 8. Anti-Patterns | ❌ Don\'t | ✅ Do | |----------|-------| | Put server data in Redux/Zustand | Use TanStack Query | | Context for high-frequency updates | Zustand / Jotai | | Global state for component UI | Local useState | | Props drilling > 3 levels | Composition or store | | Duplicate server state locally | Single source of truth | """
GitHub에서 보기