skills/react-tips/SKILL.md
10 high-impact React patterns and anti-patterns - state management, performance, hooks, and component design. Use when writing or reviewing React components.
npx skillsauth add Cst2989/react-tips-skill react-tipsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Use these patterns when writing or reviewing React code. Each one prevents real bugs or eliminates unnecessary complexity.
When multiple useState values depend on each other (loading + error + data), use useReducer instead. Prevents impossible state combinations where your UI lies to the user.
// BAD: Three setters you can forget to coordinate
setIsLoading(false);
setError(null);
setPost(data);
// GOOD: One dispatch, one guaranteed valid state
dispatch({ type: 'FETCH_SUCCESS', payload: data });
When to apply: Any time changing one piece of state requires changing another.
For search inputs and heavy filtering, use useTransition instead of debounce. It marks state updates as non-urgent so React can keep the UI responsive.
const [isPending, startTransition] = useTransition();
startTransition(() => {
setFilteredItems(filterItems(query));
});
When to apply: Any input that triggers expensive re-renders (search, filters, large lists).
Move state as close as possible to where it's consumed. If only SearchBox and SearchResults need searchTerm, wrap them in a SearchFeature component that owns the state. Siblings won't re-render.
When to apply: Before reaching for React.memo, check if the state can just be moved down the tree.
useEffect is NOT componentDidMount. It's "run this when these dependencies change." Don't use it for data fetching (use React Query/SWR). Don't put functions inside unless necessary. Extract logic out of the effect body.
When to apply: Any time you're writing a useEffect, ask: "Am I synchronizing with something external, or am I triggering logic?" If the latter, there's probably a better pattern.
key={index} causes state to stick to the wrong DOM nodes when items are added, removed, or reordered. Always use a stable, unique ID from your data (key={item.id}).
When to apply: Every list render. No exceptions.
useMemo has overhead: hook call + dependency comparison every render. For simple operations (string concat, basic math), the overhead exceeds the cost of just computing the value. Profile before memoizing.
// BAD: Memoizing a string concatenation
const fullName = useMemo(() => `${first} ${last}`, [first, last]);
// GOOD: Just compute it
const fullName = `${first} ${last}`;
When to apply: Only use useMemo for genuinely expensive computations (large array operations, complex transformations). The React Compiler will handle the rest.
A component should have one reason to change, not "do one thing." Extract data-fetching into custom hooks. Keep presentation separate from logic. When the API changes, only the hook changes. When the layout changes, only the component changes.
When to apply: When a component mixes fetching, error handling, and rendering. Split into a hook + a presentational component.
Use key on any component (not just lists) to force React to unmount and remount it. This resets all state and re-runs effects, eliminating dependency array complexity.
<UserProfile key={userId} id={userId} />
When to apply: When a component's entire state depends on a single prop (user ID, tab ID, etc.).
useEffect runs after the browser paints. useLayoutEffect runs before paint. Use useLayoutEffect when you need to measure DOM elements and immediately update styles to prevent one-frame flickers.
When to apply: Tooltips, popovers, dynamic positioning, any visual measurement + mutation.
Instead of passing config objects/arrays, build components that share state via Context. Each sub-component gets its own provider and reaches into the nearest parent's context.
<Accordion>
<Accordion.Item>
<Accordion.Header>Title</Accordion.Header>
<Accordion.Body>Content</Accordion.Body>
</Accordion.Item>
</Accordion>
When to apply: Reusable UI components (accordions, tabs, menus, dropdowns) where the consumer needs flexibility in what goes inside.
| Problem | Reach For |
|---------|-----------|
| Related state getting out of sync | useReducer |
| UI lag on input | useTransition |
| Unnecessary re-renders | State colocation |
| Data fetching in useEffect | React Query / SWR |
| List rendering bugs | Stable key from data |
| Simple computation wrapped in useMemo | Remove the useMemo |
| Component with 4+ reasons to change | Custom hook + presentation split |
| Component state tied to a single ID | key={id} prop |
| DOM measurement flicker | useLayoutEffect |
| Rigid component APIs | Compound Components |
development
Before writing useEffect, run through a decision tree to verify it's actually needed. Prevents the most common React anti-pattern in AI-generated code.
tools
Use when writing or reviewing JavaScript - prefer ES2025/ES2026 APIs (Iterator helpers, Set methods, Temporal, using, Promise.try, Error.isError, Math.sumPrecise, Map.getOrInsert) over older patterns.
development
Maintainer-only workflow for handling GitHub Secret Scanning alerts on OpenClaw. Use when Codex needs to triage, redact, clean up, and resolve secret leakage found in issue comments, issue bodies, PR comments, or other GitHub content.
development
Maintainer workflow for OpenClaw releases, prereleases, changelog release notes, and publish validation. Use when Codex needs to prepare or verify stable or beta release steps, align version naming, assemble release notes, check release auth requirements, or validate publish-time commands and artifacts.