How to review React pull requests: the 9 defect classes with examples
React bugs rarely throw. A stale closure, a key that is not stable or a memo that never holds renders a screen that looks right in the demo and drifts in production. This guide walks DiffDojo's nine defect classes as they appear in React components and hooks.
1. Security
dangerouslySetInnerHTML with user content is XSS (stored, if the content comes from the database) unless the HTML is sanitized first; a markdown renderer that allows raw HTML does not count. In Next.js, check that secrets never get a NEXT_PUBLIC_ prefix and are never passed as props from a server component to a client component; both ship them to the browser.
- Secret in a client component: the billable key is either undefined or public
- Stored XSS: one malicious bio runs script in every visitor's browser
2. Requirement fidelity
Check the sort key, the filter and the empty state against the ticket. A list sorted by the wrong field or a control the spec promised but the component cannot render is easy to approve when the UI looks finished.
3. Edge cases & error handling
Look at loading, error and empty states, and at what happens when two requests race: an older response can arrive last and overwrite newer results unless the effect ignores or aborts stale requests. Fast typing in a search box is the classic trigger, so play two quick requests through the code before approving.
4. Hallucinated APIs & dependencies
A hook React never shipped (useMount, for example) fails at build time or on first render, but a prop a library does not accept is often silently ignored, so the feature just does nothing. Check every hook and prop you do not recognise against the docs.
5. State & data flow
Watch effect dependency arrays, closures that capture old state in timers and intervals, and derived values copied into state that then go out of sync with their source. A missing dependency or a stale closure rarely fails a test, so read each useEffect and useCallback against the values it actually uses.
6. Performance
React.memo only helps when props are stable: a new callback or object literal on every render re-renders every memoized row anyway. Also look for expensive work done on every render instead of being memoized.
7. Data correctness
Dates and money in the browser: new Date('2026-03-15') parses as UTC midnight and can show the previous day in western time zones, and floating-point arithmetic on prices drifts. Parse date-only strings explicitly and keep money in integer minor units until it is formatted for display.
8. Deceptive tests
Tests that assert a mocked hook's return value, or check only that a component renders, pass when the logic is wrong. Look for at least one assertion on the behaviour the PR changes.
9. Over-engineering
A context provider, a reducer and a custom hook to hold one boolean is a review comment. Ask for local state until something needs more.
The checklist
- Does any user content reach the DOM unsanitized, or a secret reach the browser?
- Do sort, filter and empty states match the ticket?
- Loading, error, empty and racing requests: all handled?
- Does every hook and prop exist in the installed version?
- Are effect dependencies complete, and closures free of stale state?
- Are props to memoized components stable?
- Are dates parsed in the intended time zone, and money kept in integers?
- Would a test fail if the behaviour broke?
- Is the state as local as it can be?
As of October 2026, the DiffDojo library has 46 live React PRs to practise on, each graded on whether you catch the planted bug or approve the clean one.
Practise
Review a React PR with the answer hidden.
Leave inline comments and a verdict, then see what you caught. Free, no signup.
Open the React library →