install
source · Clone the upstream repo
git clone https://github.com/aiskillstore/marketplace
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/aiskillstore/marketplace "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/barnhardt-enterprises-inc/state-management" ~/.claude/skills/aiskillstore-marketplace-state-management && rm -rf "$T"
manifest:
skills/barnhardt-enterprises-inc/state-management/SKILL.mdsource content
State Management
Philosophy
- Server State → TanStack Query
- Client State → Zustand
- Form State → React Hook Form + Zod
- URL State → nuqs or searchParams
TanStack Query
Query Keys Factory
export const userKeys = { all: ['users'] as const, lists: () => [...userKeys.all, 'list'] as const, list: (filters: Filters) => [...userKeys.lists(), filters] as const, details: () => [...userKeys.all, 'detail'] as const, detail: (id: string) => [...userKeys.details(), id] as const, };
Hooks Pattern
export function useUsers(filters?: Filters) { return useQuery({ queryKey: userKeys.list(filters ?? {}), queryFn: () => getUsers(filters), }); } export function useUser(id: string) { return useQuery({ queryKey: userKeys.detail(id), queryFn: () => getUser(id), enabled: !!id, }); } export function useCreateUser() { const queryClient = useQueryClient(); return useMutation({ mutationFn: createUser, onSuccess: () => { queryClient.invalidateQueries({ queryKey: userKeys.lists() }); }, }); } export function useUpdateUser() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, data }: { id: string; data: UpdateUserInput }) => updateUser(id, data), onSuccess: (_, { id }) => { queryClient.invalidateQueries({ queryKey: userKeys.detail(id) }); queryClient.invalidateQueries({ queryKey: userKeys.lists() }); }, }); }
Zustand
Typed Store
interface UIStore { sidebarOpen: boolean; theme: 'light' | 'dark'; toggleSidebar: () => void; setTheme: (theme: 'light' | 'dark') => void; } export const useUIStore = create<UIStore>((set) => ({ sidebarOpen: true, theme: 'light', toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })), setTheme: (theme) => set({ theme }), }));
Performance: Use Selectors
// CORRECT - Only re-renders when sidebarOpen changes const sidebarOpen = useUIStore((s) => s.sidebarOpen); // WRONG - Re-renders on ANY state change const { sidebarOpen } = useUIStore();
Persist Middleware
import { persist } from 'zustand/middleware'; export const useSettingsStore = create<SettingsStore>()( persist( (set) => ({ language: 'en', setLanguage: (language) => set({ language }), }), { name: 'settings-storage', } ) );
Computed Values with Selectors
// Create a selector const selectFilteredItems = (state: Store) => state.items.filter(item => item.active); // Use in component const filteredItems = useStore(selectFilteredItems);
Form State: React Hook Form + Zod
import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; const schema = z.object({ name: z.string().min(1, 'Required'), email: z.string().email('Invalid email'), }); type FormData = z.infer<typeof schema>; export function UserForm() { const { register, handleSubmit, formState: { errors } } = useForm<FormData>({ resolver: zodResolver(schema), }); const onSubmit = (data: FormData) => { // data is typed and validated }; return ( <form onSubmit={handleSubmit(onSubmit)}> <input {...register('name')} /> {errors.name && <span>{errors.name.message}</span>} <input {...register('email')} /> {errors.email && <span>{errors.email.message}</span>} <button type="submit">Submit</button> </form> ); }