Use when reviewing React component code during code quality review, when the diff contains .tsx or .jsx files, or when reviewing components that use React hooks, state management, or data fetching patterns
Supplement to the standard code review checklist. Covers judgment calls that linters and static analysis cannot catch.
During code review when the diff contains React code (.tsx, .jsx, or .ts files importing from "react").
Apply these checks after the standard code quality review, as an additional pass.
Check each section below. Flag violations as Important or Minor per the standard review severity levels.
A component is too large when it mixes concerns, not when it exceeds a line count.
Flag when a single component does more than one of:
useState + useEffect)The fix pattern:
useOrders, useAuth, useSettings). A hook is "complex" when it combines multiple useState calls that interact with each other or with useEffect.// BAD: all mixed in one component
function Dashboard() {
const [orders, setOrders] = useState([]);
const [sorted, setSorted] = useState([]);
useEffect(() => { fetch(/*...*/) }, []);
useEffect(() => { setSorted(orders.sort(/*...*/)) }, [orders]);
const formatPrice = (n: number) => /*...*/;
return <table>...</table>;
}
// GOOD: separated by responsibility
function Dashboard() {
const { orders, sort, sortField } = useOrders(); // custom hook: state + fetching
return <OrdersTable orders={orders} onSort={sort} sortField={sortField} />;
}
// sortOrders(), formatPrice() are pure functions in a utils file
Linters catch missing dependencies. They cannot catch unnecessary effects.
Flag these patterns:
| Pattern | Fix |
|---|---|
| Effect computes derived state (useEffect + setState from other state) | Compute during render or useMemo |
| Effect resets state when a prop changes | Use key on the component instead |
| Chain of effects (A sets state, triggers B, triggers C) | Do the work in one event handler |
| User-driven side effect in useEffect instead of event handler | Move to the handler that caused it |
| Effect without cleanup for subscriptions, timers, or fetch | Add cleanup function with AbortController |
useEffect is ONLY for synchronizing with external systems (subscriptions, timers, DOM manipulation, third-party widgets).
Check package.json for the React version. Review for patterns appropriate to that version.
React 19+ apps should use:
| Instead of | Use |
|---|---|
| Manual loading/error/data state for fetches | <Suspense> boundaries + Server Components for data fetching |
| onSubmit + useState for form submission state | useActionState for form action + state, useFormStatus for pending UI |
| useEffect to resolve promises | use() hook |
| Defensive React.memo, useMemo, useCallback | React Compiler handles memoization automatically. Only add manual memoization when a third-party library requires stable identity, or as an explicit useEffect dependency. |
useActionState signature reminder: (prevState, formData) => newState — first arg is previous state, not formData. Imported from "react", not "react-dom".
useFormStatus rule: Must be called from a child component rendered inside the <form>, not in the same component that renders the form.
eslint-plugin-jsx-a11y catches static markup issues. These behavioral issues require judgment:
Focus management:
role="alert" or aria-live="polite" so screen readers announce themKeyboard navigation:
<div onClick> is never a button — use <button> (linter catches some, not all)Color:
Query priority (most to least preferred):
getByRole — validates accessibility at the same timegetByLabelText — best for form fieldsgetByText — for non-interactive contentgetByTestId — last resort onlyInteraction:
userEvent (simulates real user behavior: focus, keyboard, hover) not fireEvent (dispatches a single DOM event)What to test:
Mocking:
global.fetch or mocking component importsSearch for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer