React code review checklist
React code review often misses bugs in tests, async updates and rendering. This checklist covers what Revix flags in real pull requests. Look for tests in a synchronous act(), state updates lost from a Transition after an await, and render code with a different result each run. Each rule shows the code to flag, the fix and its source.
- Use asynchronous act
- Re-enter a Transition after await
- Keep render calculations idempotent
- Do not use React.cloneElement
- Do not mutate external values during render
- Keep side effects out of render
- Avoid updates during render
- Prefer useEffect for non-layout work
- Associate every input with a label
- Do not suppress the react-hooks/exhaustive-deps linter rule
- Use callback refs for dynamic node lists
11 rules to check in every React pull request
Revix reviews React pull requests against 487 rules. This page shows a short list we picked from them. Each rule has a bad example, a good example and a source. We check each rule against its source and update the list every month. Rules marked as flagged by Revix have come up in real reviews.
Use asynchronous act
Synchronous act may leave updates unflushed across scheduling boundaries, making tests unreliable.
Avoid
act(() => {
root.render(<App />);
});Prefer
await act(async () => {
root.render(<App />);
});Flagged by Revix in real reviews · Deprecation · All current versions · Source: act
Re-enter a Transition after await
Without the nested call, the post-await update is scheduled as a regular update rather than as a Transition.
Avoid
startTransition(async () => {
await save();
setPage('/done');
});Prefer
startTransition(async () => {
await save();
startTransition(() => setPage('/done'));
});Flagged by Revix in real reviews · API usage · All current versions · Source: React doesn't treat my state update after await as a Transition
Keep render calculations idempotent
React may run rendering multiple times or pause and resume it. Non-idempotent calculations can produce inconsistent or stale UI.
Avoid
function Clock() { return <span>{new Date().toLocaleString()}</span>; }Prefer
const [time, setTime] = useState(() => new Date());Flagged by Revix in real reviews · Best practice · All current versions · Source: Components and Hooks must be pure
Do not use React.cloneElement
Continued use of cloneElement can cause fragile and harder-to-maintain code; newer patterns are more declarative.
Avoid
React.cloneElement(child, {prop: value})Prefer
Use props composition or render props pattern instead of cloning elements.Flagged by Revix in real reviews · Deprecation · All current versions · Source: Legacy React APIs
Do not mutate external values during render
Persistent mutations are repeated when React renders again and can accumulate duplicated data or affect other components.
Avoid
const items = []; function List({ friends }) { friends.forEach(f => items.push(f)); return items; }Prefer
function List({ friends }) { const items = []; friends.forEach(f => items.push(f)); return items; }Flagged by Revix in real reviews · Anti-pattern · All current versions · Source: Components and Hooks must be pure
Keep side effects out of render
React can render components multiple times, so an untracked side effect may run repeatedly or at an unexpected time.
Avoid
function Panel() { analytics.track('panel-rendered'); return <PanelView />; }Prefer
function Panel() { useEffect(() => analytics.track('panel-shown'), []); return <PanelView />; }Flagged by Revix in real reviews · Best practice · All current versions · Source: Components and Hooks must be pure
Avoid updates during render
React documents cascading updates as a performance-regression pattern because work completed in the current render pass may be discarded.
Avoid
function Panel() {
if (needsRefresh) setReady(true);
return <View />;
}Prefer
function Panel() {
const ready = !needsRefresh;
return <View ready={ready} />;
}Flagged by Revix in real reviews · Performance · All current versions · Source: React Performance tracks
Prefer useEffect for non-layout work
React explicitly warns that useLayoutEffect can hurt performance and recommends useEffect when possible.
Avoid
useLayoutEffect(() => { fetchUser(); }, []);Prefer
useEffect(() => { fetchUser(); }, []);Flagged by Revix in real reviews · Performance · All current versions · Source: useLayoutEffect
Associate every input with a label
Without an association, screen readers do not announce the field's caption when it receives focus, and clicking the caption will not focus the input.
Avoid
<label>Your age:</label><input id="age" />Prefer
<label htmlFor={ageId}>Your age:</label><input id={ageId} />Flagged by Revix in real reviews · Best practice · All current versions · Source: <input>
Do not suppress the react-hooks/exhaustive-deps linter rule
The linter helps catch mistakes that are otherwise hard to detect. Ignoring it amounts to lying to React about effect dependencies, making behavior unpredictable.
Avoid
useEffect(() => { doSomething(); }, []); // eslint-ignore-next-line react-hooks/exhaustive-depsPrefer
useEffect(() => { doSomething(); }, [dep1, dep2]); // all dependencies included and no lint suppressionFlagged by Revix in real reviews · Anti-pattern · All current versions · Source: React useEffect documentation
Use callback refs for dynamic node lists
DOM structure changes can make selector-based lookup brittle or cause it to target the wrong nodes.
Avoid
const listRef = useRef(null);
const item = listRef.current.querySelectorAll('li')[index];Prefer
ref={node => {
nodes.current.set(item.id, node);
return () => nodes.current.delete(item.id);
}}Flagged by Revix in real reviews · Anti-pattern · All current versions · Source: Manipulating the DOM with Refs
Deprecated React APIs to flag in review
These rules catch code that still works today but leans on an API that React has deprecated or replaced.
How Revix applies these rules in pull requests
Revix matches these rules against the code each pull request changes. When a comment rests on one, it names the rule, so the author can read the reasoning and the source instead of taking the bot's word for it.

revixappdevBot
suggestion: Synchronous act may leave updates unflushed across scheduling boundaries, making tests unreliable.
📚 Rule (React): Use asynchronous act
Keep reading
- How to Write a Great PR Review CommentMost PR comments waste the author's time because they never say what they actually want. Here is the four-part format that fixes that.
- Repo-Aware AI Code Review vs. Diff-Based ReviewRepo-aware vs diff-based AI code review: diff review reads only the patch. Repo-aware review catches broken conventions, duplicate abstractions, and drift.
Revix also learns your team's own rules from the code you already merged. Start a free trial and see them on your next pull request.