Skip to main content

react

Use when building advanced React features — designing custom hooks, using Suspense or error boundaries, working in Next.js App Router with Server Components or Server Actions, applying TypeScript generics, or building animated UI with Framer Motion, View Transitions, or scroll-driven animations.

Source facts

Repository
kid-sid/codex-spellbook
Last source activity
May 11, 2026 at 18:04
Detected SKILL.md language
English
Stars
22
Forks
1

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
react
description
Use when building advanced React features — designing custom hooks, using Suspense or error boundaries, working in Next.js App Router with Server Components or Server Actions, applying TypeScript generics, or building animated UI with Framer Motion, View Transitions, or scroll-driven animations.
# React — Advanced Patterns > For state management (Zustand/Redux), TanStack Query, React Hook Form, React Router, and component testing, see the `frontend` skill. ## When to Activate - Designing or debugging custom hooks - Choosing between Context, Zustand, or prop drilling - Using Suspense, error boundaries, or concurrent features (`useTransition`, `useDeferredValue`) - Building compound components or headless/renderless components - Working in Next.js App Router (Server Components, Server Actions, streaming) - TypeScript generics, event types, or `forwardRef` patterns - `useRef`, `useImperativeHandle`, portals, or advanced DOM integration --- ## Hooks Deep Dive ### useReducer — when useState gets complex ```tsx type State = { count: number; error: string | null; loading: boolean }; type Action = | { type: "increment" } | { type: "set_error"; payload: string } | { type: "reset" }; function reducer(state: State, action: Action): State { switch (action.type) { case "increment": return { ...state, count: state.count + 1 }; case "set_error": return { ...state, error: action.payload, loading: false }; case "reset": return { count: 0, error: null, loading: false }; } } const [state, dispatch] = useReducer(reducer, { count: 0, error: null, loading: false }); dispatch({ type: "increment" }); ``` Use `useReducer` over `useState` when: multiple related state fields, next state depends on previous, or actions have semantic names that make logic readable. ### useRef — three distinct uses ```tsx // 1. DOM access const inputRef = useRef<HTMLInputElement>(null); useEffect(() => { inputRef.current?.focus(); }, []); // 2. Mutable value that doesn't trigger re-render (e.g. interval ID, previous value) const timerRef = useRef<NodeJS.Timeout | null>(null); const prevValueRef = useRef(value); useEffect(() => { prevValueRef.current = value; }); // 3. Stable callback reference (avoids stale closure in event listeners) const callbackRef = useRef(onSave); useEffect(() => { callbackRef.current = onSave; }); useEffect(() => { const handler = () => callbackRef.current(); window.addEventListener("keydown", handler); return () => window.removeEventListener("keydown", handler); }, []); // dep array stays empty — no stale closure ``` ### useContext — subscribe only to what you need ```tsx // Split large contexts to prevent unnecessary re-renders const UserDataContext = createContext<UserData | null>(null); const UserActionsContext = createContext<UserActions | null>(null); // Custom hook enforces non-null and co-locates error message function useUserData() { const ctx = useContext(UserDataContext); if (!ctx) throw new Error("useUserData must be used inside UserProvider"); return ctx; } // Provider combines both function UserProvider({ children }: { children: ReactNode }) { const [user, setUser] = useState<UserData | null>(null); const actions = useMemo(() => ({ login: ..., logout: ... }), []); // stable ref return ( <UserDataContext.Provider value={user}> <UserActionsContext.Provider value={actions}> {children} </UserActionsContext.Provider> </UserDataContext.Provider> ); } ``` **Context is NOT a performance-free global store.** Every consumer re-renders when the value changes. Use Zustand for frequently-changing shared state; Context for stable config (theme, locale, auth user). ### Concurrent Features ```tsx // useTransition — mark state update as non-urgent (keeps UI responsive) const [isPending, startTransition] = useTransition(); function handleSearch(query: string) { setInputValue(query); // urgent — update input immediately startTransition(() => { setFilteredResults(filter(query)); // non-urgent — can be interrupted }); } // useDeferredValue — defer a derived value (use when you don't own the state setter) const deferredQuery = useDeferredValue(searchQuery); const results = useMemo(() => filter(deferredQuery), [deferredQuery]); const isStale = searchQuery !== deferredQuery; // show loading indicator // useId — stable unique IDs across server/client (avoids hydration mismatch) function FormField({ label }: { label: string }) { const id = useId(); return ( <> <label htmlFor={id}>{label}</label> <input id={id} /> </> ); } ``` --- ## Custom Hooks Extract logic into a hook when: the same `useEffect` + `useState` combo appears twice, or a component mixes UI with data-fetching concerns. ```tsx // useLocalStorage — syncs state with localStorage function useLocalStorage<T>(key: string, initialValue: T) { const [value, setValue] = useState<T>(() => { try { const stored = localStorage.getItem(key); return stored ? JSON.parse(stored) : initialValue; } catch { return initialValue; } }); const set = useCallback((newValue: T | ((prev: T) => T)) => { setValue(prev => { const next = newValue instanceof Function ? newValue(prev) : newValue; localStorage.setItem(key, JSON.stringify(next)); return next; }); }, [key]); return [value, set] as const; } // useDebounce — delay reacting to fast-changing input function useDebounce<T>(value: T, delay: number): T { const [debounced, setDebounced] = useState(value); useEffect(() => { const timer = setTimeout(() => setDebounced(value), delay); return () => clearTimeout(timer); }, [value, delay]); return debounced; } // useEventListener — typed, auto-cleaned function useEventListener<K extends keyof WindowEventMap>( event: K, handler: (e: WindowEventMap[K]) => void, element: EventTarget = window, ) { const handlerRef = useRef(handler); useEffect(() => { handlerRef.current = handler; }); useEffect(() => { const fn = (e: Event) => handlerRef.current(e as WindowEventMap[K]); element.addEventListener(event, fn); return () => element.removeEventListener(event, fn); }, [event, element]); } ``` --- ## Compound Components Let parent manage state; children access it via Context. No prop drilling, flexible composition. ```tsx const TabsContext = createContext<{ active: string; setActive: (id: string) => void } | null>(null); const useTabs = () => { const ctx = useContext(TabsContext); if (!ctx) throw new Error("Must be used inside <Tabs>"); return ctx; }; function Tabs({ defaultTab, children }: { defaultTab: string; children: ReactNode }) { const [active, setActive] = useState(defaultTab); return ( <TabsContext.Provider value={{ active, setActive }}> <div>{children}</div> </TabsContext.Provider> ); } function Tab({ id, children }: { id: string; children: ReactNode }) { const { active, setActive } = useTabs(); return ( <button role="tab" aria-selected={active === id} onClick={() => setActive(id)} > {children} </button> ); } function TabPanel({ id, children }: { id: string; children: ReactNode }) { const { active } = useTabs(); return active === id ? <div role="tabpanel">{children}</div> : null; } Tabs.Tab = Tab; Tabs.Panel = TabPanel; // Usage <Tabs defaultTab="overview"> <Tabs.Tab id="overview">Overview</Tabs.Tab> <Tabs.Tab id="settings">Settings</Tabs.Tab> <Tabs.Panel id="overview"><OverviewContent /></Tabs.Panel> <Tabs.Panel id="settings"><SettingsContent /></Tabs.Panel> </Tabs> ``` --- ## Error Boundaries React errors during render are caught by the nearest error boundary. Must be a class component (or use `react-error-boundary` library). ```tsx import { ErrorBoundary } from "react-error-boundary"; function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) { return ( <div role="alert"> <p>Something went wrong:</p> <pre>{error.message}</pre> <button onClick={resetErrorBoundary}>Try again</button> </div> ); } // Wrap any subtree that might throw during render <ErrorBoundary FallbackComponent={ErrorFallback} onReset={() => queryClient.resetQueries()} onError={(error, info) => logger.error(error, info)} > <UserDashboard /> </ErrorBoundary> ``` Error boundaries do **not** catch: async errors (use `try/catch`), event handler errors, or server-side errors. --- ## Suspense ```tsx // Suspense shows fallback while children are loading (lazy imports or data) <Suspense fallback={<Spinner />}> <LazyComponent /> </Suspense> // Nested Suspense — granular loading states <Suspense fallback={<PageSkeleton />}> <PageHeader /> <Suspense fallback={<TableSkeleton />}> <DataTable /> {/* streams in independently */} </Suspense> </Suspense> // Combine with ErrorBoundary <ErrorBoundary FallbackComponent={ErrorFallback}> <Suspense fallback={<Spinner />}> <AsyncComponent /> </Suspense> </ErrorBoundary> ``` --- ## forwardRef and useImperativeHandle ```tsx // forwardRef — pass ref through to a DOM element const Input = forwardRef<HTMLInputElement, InputProps>(function Input(props, ref) { return <input ref={ref} {...props} />; }); // useImperativeHandle — expose a custom API instead of the raw DOM node interface DialogHandle { open: () => void; close: () => void } const Dialog = forwardRef<DialogHandle, DialogProps>(function Dialog(props, ref) { const [open, setOpen] = useState(false); useImperativeHandle(ref, () => ({ open: () => setOpen(true), close: () => setOpen(false), })); return open ? <div>{props.children}</div> : null; }); // Usage const dialogRef = useRef<DialogHandle>(null); dialogRef.current?.open(); ``` --- ## Portals Render outside the component tree (modals, tooltips, toasts) without CSS stacking-context issues. ```tsx import { createPortal } from "react-dom"; function Modal({ children, onClose }: ModalProps) { return createPortal( <div className="modal-overlay" onClick={onClose}> <div className="modal-content" onClick={e => e.stopPropagation()}> {children} </div> </div>, document.body, // renders at the bottom of <body>, outside the app root ); } ``` --- ## TypeScript Patterns ```tsx // Generic component function List<T extends { id: string }>({ items, renderItem, }: { items: T[]; renderItem: (item: T) => ReactNode; }) { return <ul>{items.map(item => <li key={item.id}>{renderItem(item)}</li>)}</ul>; } // Event types const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => setValue(e.target.value); const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault(); ... }; const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => { ... }; // Polymorphic component (renders as any element) type ButtonProps<T extends ElementType = "button"> = { as?: T; children: ReactNode; } & ComponentPropsWithoutRef<T>; function Button<T extends ElementType = "button">({ as, children, ...props }: ButtonProps<T>) { const Component = as ?? "button"; return <Component {...props}>{children}</Component>; }
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub