--- id: JosiahSiegel/claude-plugin-marketplace/react-state-management version: "9039bb54" license: MIT install: manual updated: 2026-06-18 --- # react-state-management — React State Management guides you through selecting and implementing the ideal state solution for your architecture. It covers built-in hooks, Context optimization, and third-party libraries like Zustand, Jotai, TanStack Query, and SWR—helping you avoid prop drilling and scale effectively. Publisher: JosiahSiegel · Stars: 49 · Updated: 2026-06-18 Install (manual): `git clone https://github.com/JosiahSiegel/claude-plugin-marketplace` ## SKILL.md ## Quick Reference | Library | Best For | Install | |---------|----------|---------| | Context | Small apps, themes | Built-in | | Zustand | Simple global state | `npm i zustand` | | Jotai | Atomic/granular state | `npm i jotai` | | TanStack Query | Server state/caching | `npm i @tanstack/react-query` | | SWR | Data fetching | `npm i swr` | | Scenario | Recommended | |----------|-------------| | Simple local state | `useState` | | Complex local state | `useReducer` | | Shared state (small app) | Context + useReducer | | Shared state (large app) | Zustand or Jotai | | Server state | TanStack Query or SWR | ## When to Use This Skill Use for **state management decisions**: - Choosing between state management solutions - Setting up Zustand, Jotai, or Context stores - Configuring TanStack Query for server state - Implementing optimistic updates - Normalizing complex state structures - Avoiding unnecessary re-renders **For React hooks basics**: see `react-hooks-complete` --- # React State Management ## Built-in State Management ### Component State with useState ```tsx 'use client'; import { useState } from 'react'; function ShoppingCart() { const [items, setItems] = useState([]); const [isOpen, setIsOpen] = useState(false); const addItem = (product: Product) => { setItems((prev) => { const existing = prev.find((item) => item.id === product.id); if (existing) { return prev.map((item) => item.id === product.id ? { ...item, quantity: item.quantity + 1 } : item ); } return [...prev, { ...product, quantity: 1 }]; }); }; const total = items.reduce( (sum, item) => sum + item.price * item.quantity, 0 ); return (
{isOpen && }
); } ``` ### Complex State with useReducer ```tsx 'use client'; import { useReducer, Dispatch, createContext, useContext } from 'react'; // Types interface CartState { items: CartItem[]; isLoading: boolean; error: string | null; } type CartAction = | { type: 'ADD_ITEM'; payload: Product } | { type: 'REMOVE_ITEM'; payload: string } | { type: 'UPDATE_QUANTITY'; payload: { id: string; quantity: number } } | { type: 'CLEAR_CART' } | { type: 'SET_LOADING'; payload: boolean } | { type: 'SET_ERROR'; payload: string }; // Reducer function cartReducer(state: CartState, action: CartAction): CartState { switch (action.type) { case 'ADD_ITEM': { const existing = state.items.find( (item) => item.id === action.payload.id ); if (existing) { return { ...state, items: state.items.map((item) => item.id === action.payload.id ? { ...item, quantity: item.quantity + 1 } : item ), }; } return { ...state, items: [...state.items, { ...action.payload, quantity: 1 }], }; } case 'REMOVE_ITEM': return { ...state, items: state.items.filter((item) => item.id !== action.payload), }; case 'UPDATE_QUANTITY': return { ...state, items: state.items.map((item) => item.id === action.payload.id ? { ...item, quantity: action.payload.quantity } : item ), }; case 'CLEAR_CART': return { ...state, items: [] }; case 'SET_LOADING': return { ...state, isLoading: action.payload }; case 'SET_ERROR': return { ...state, error: action.payload }; default: return state; } } // Context const CartContext = createContext<{ state: CartState; dispatch: Dispatch; } | null>(null); // Provider export function CartProvider({ children }: { children: React.ReactNode }) { const [state, dispatch] = useReducer(cartReducer, { items: [], isLoading: false, error: null, }); return ( {children} ); } // Hook export function useCart() { const context = useContext(CartContext); if (!context) { throw new Error('useCart must be used within CartProvider'); } return context; } ``` ## Context API ### Creating and Using Context ```tsx import { createContext, useContext, useState, ReactNode } from 'react'; // Theme context interface Theme { colors: { primary: string; secondary: string; background: string }; spacing: { sm: number; md: number; lg: number }; } interface ThemeContextType { theme: Theme; setTheme: (theme: Theme) => void; toggleDarkMode: () => void; isDark: boolean; } const ThemeContext = createContext(null); const lightTheme: Theme = { colors: { primary: '#3b82f6', secondary: '#8b5cf6', background: '#ffffff' }, spacing: { sm: 8, md: 16, lg: 24 }, }; const darkTheme: Theme = { colors: { primary: '#60a5fa', secondary: '#a78bfa', background: '#1f2937' }, spacing: { sm: 8, md: 16, lg: 24 }, }; export function ThemeProvider({ children }: { children: ReactNode }) { const [isDark, setIsDark] = useState(false); const [theme, setTheme] = useState(lightTheme); const toggleDarkMode = () => { setIsDark((prev) => !prev); setTheme(isDark ? lightTheme : darkTheme); }; return ( {children} ); } export function useTheme() { const context = useContext(ThemeContext); if (!context) { throw new Error('useTheme must be used within ThemeProvider'); } return context; } ``` ### Optimizing Context Performance ```tsx import { createContext, useContext, useMemo, useCallback, useState } from 'react'; // Split context to prevent unnecessary re-renders const UserContext = createContext(null); const UserActionsContext = createContext<{ login: (email: string, password: string) => Promise; logout: () => void; updateProfile: (data: Partial) => Promise; } | null>(null); export function UserProvider({ children }: { children: ReactNode }) { const [user, setUser] = useState(null); const login = useCallback(async (email: string, password: string) => { const response = await fetch('/api/login', { method: 'POST', body: JSON.stringify({ email, password }), }); const userData = await response.json(); setUser(userData); }, []); const logout = useCallback(() => { setUser(null); }, []); const updateProfile = useCallback(async (data: Partial) => { const response = await fetch('/api/profile', { method: 'PATCH', body: JSON.stringify(data), }); const updated = await response.json(); setUser(updated); }, []); // Memoize actions object const actions = useMemo( () => ({ login, logout, updateProfile }), [login, logout, updateProfile] ); return ( {children} ); } // Separate hooks for data and actions export function useUser() { return useContext(UserContext); } export function useUserActions() { const context = useContext(UserActionsContext); if (!context) { throw new Error('useUserActions must be used within UserProvider'); } return context; } ``` ## Zustand ### Basic Zustand Store ```tsx import { create } from 'zustand'; import { persist, devtools } from 'zustand/middleware'; interface CartStore { items: CartItem[]; addItem: (product: Product) => void; removeItem: (id: string) => void; updateQuantity: (id: string, quantity: number) => void; clearCart: () => void; total: () => number; } export const useCartStore = create()( devtools( persist( (set, get) => ({ items: [], addItem: (product) => set((state) => { const existing = state.items.find((item) => item.id === product.id); if (existing) { return { items: state.items.map((item) => item.id === product.id ? { ...item, quantity: item.quantity + 1 } : item ), }; } return { items: [...state.items, { ...product, quantity: 1 }] }; }), removeItem: (id) => set((state) => ({ items: state.items.filter((item) => item.id !== id), })), updateQuantity: (id, quantity) => set((state) => ({ items: state.items.map((item) => item.id === id ? { ...item, quantity } : item ), })), clearCart: () => set({ items: [] }), total: () => get().items.reduce( (sum, item) => sum + item.price * item.quantity, 0 ), }), { name: 'cart-storage' } ) ) ); // Usage in component function CartButton() { const items = useCartStore((state) => state.items); const total = useCartStore((state) => state.total()); return ( ); } ``` ### Zustand with Immer ```tsx import { create } from 'zustand'; import { immer } from 'zustand/middleware/immer'; interface TodoStore { todos: Todo[]; addTodo: (text: string) => void; toggleTodo: (id: string) => void; deleteTodo: (id: string) => void; } export const useTodoStore = create()( immer((set) => ({ todos: [], addTodo: (text) => set((state) => { state.todos.push({ id: crypto.randomUUID(), text, completed: false, }); }), toggleTodo: (id) => set((state) => { const todo = state.todos.find((t) => t.id === id); if (todo) { todo.completed = !todo.completed; } }), deleteTodo: (id) => set((state) => { const index = state.todos.findIndex((t) => t.id === id); if (index !== -1) { state.todos.splice(index, 1); } }), })) ); ``` ## Jotai and TanStack Query Detailed patterns for Jotai atom-based state and TanStack Query server-state management — atoms, derived atoms, async atoms, query clients, mutations, invalidation, optimistic updates, and cache tuning — live in `references/jotai-and-tanstack-query.md`. Load that reference when choosing atom composition or production-grade server-state caching. ## SWR ### Basic SWR Usage ```tsx import useSWR, { SWRConfig } from 'swr'; const fetcher = (url: string) => fetch(url).then((res) => res.json()); function App() { return ( ); } function Dashboard() { const { data, error, isLoading, mutate } = useSWR('/api/dashboard'); if (error) return
Failed to load
; if (isLoading) return
Loading...
; return (

Dashboard

Total Users: {data.totalUsers}

); } ``` ### SWR Mutation ```tsx import useSWRMutation from 'swr/mutation'; async function createUser(url: string, { arg }: { arg: CreateUserInput }) { const res = await fetch(url, { method: 'POST', body: JSON.stringify(arg), }); return res.json(); } function CreateUserForm() { const { trigger, isMutating } = useSWRMutation('/api/users', createUser); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); const formData = new FormData(e.currentTarget); await trigger({ name: formData.get('name') as string, email: formData.get('email') as string, }); }; return (
); } ``` ## Best Practices ### 1. Choose the Right Tool | Scenario | Recommended | |----------|-------------| | Simple local state | useState | | Complex local state | useReducer | | Shared state (small app) | Context + useReducer | | Shared state (large app) | Zustand or Jotai | | Server state | TanStack Query or SWR | ### 2. Avoid Prop Drilling ```tsx // Instead of passing props through many levels // Use context or state management {/* Access user via useUser() */} ``` ### 3. Normalize Complex State ```tsx // Instead of nested objects const badState = { posts: [ { id: 1, title: 'Post 1', author: { id: 1, name: 'Alice' } }, { id: 2, title: 'Post 2', author: { id: 1, name: 'Alice' } }, ], }; // Use normalized structure const goodState = { posts: { byId: { 1: { id: 1, title: 'Post 1', authorId: 1 } }, allIds: [1, 2], }, authors: { byId: { 1: { id: 1, name: 'Alice' } }, allIds: [1], }, }; ``` ## Additional References For detailed patterns and advanced use cases, see: - `references/zustand-patterns.md` - Advanced Zustand patterns including slices, middleware, and testing [View on SkillFed](https://skillfed.io/JosiahSiegel/claude-plugin-marketplace/react-state-management) · [View on GitHub](https://github.com/JosiahSiegel/claude-plugin-marketplace)