Skip to content

Review checklist

This is the checklist to run down — by eye, on any diff, yours or an AI’s — after the tools in Verification Tools have run and come back clean. It fits on one printed page on purpose: a checklist nobody actually opens during a review is worthless. Every line links to the lesson that explains why, so this page stays a list, not a re-run of the course. The groups follow the same order as How to read React you did not write.

flowchart LR
  H["Hooks and deps"] --> P["Partial tool coverage<br/>(eslint-plugin-react-hooks catches some; behavior needs a test)"]
  B["Boundary and directives"] --> M["Mostly human review<br/>(a real RSC framework build is the only real check)"]
  K["Keys and identity"] --> M
  S["State ownership"] --> P
  E["Effects vs events"] --> M
  C["Memo and compiler"] --> M
What actually catches each group, if anything
  • Does every useEffect/useMemo/useCallback dependency array include everything the callback actually reads? See useEffect & Effects.
  • Is a value that changes often (a callback, a theme) read through useEffectEvent rather than silently omitted from an effect’s deps? See the AI Bug Catalog, entry 2.
  • Is every hook called unconditionally, at the top level, in the same order every render — never after an early return, never inside an if? See the AI Bug Catalog, entry 3.
  • Does useRef<T>() always receive an explicit initial value (null for a DOM ref)? See TypeScript with React.
  • Does new code accept ref as a plain prop instead of wrapping itself in forwardRef? See useRef & the DOM.
  • Does every useSyncExternalStore’s getSnapshot return a primitive or a cached reference, never a fresh object/array literal? See External Stores.
  • Is every promise passed to use() read from a stable cache, never built fresh inline in the render body? See use() & Data Fetching.
  • Does the code import useActionState from react, not useFormState from react-dom? See the AI Bug Catalog, entry 8.
  • Does every file that calls a client-only hook (useState, useEffect) or attaches an event handler actually have 'use client' on itself or a parent, in a real RSC-capable project? See Server Components.
  • Does every prop crossing from a Server Component into a Client Component stay plain, serializable data (or a real Server Action) — never a bare function or class instance? See Server Components.
  • Is dangerouslySetInnerHTML ever fed user-supplied content without running it through a real sanitizer first? See the AI Bug Catalog, entry 11.
  • Is useOptimistic’s setter (or requestFormReset) called from inside startTransition/an Action, never directly from a plain event handler? See Forms & useOptimistic.
  • Does every .map() over a list use a stable id from the data as key — never the array index, and never omitted entirely? See the AI Bug Catalog, entries 13–14.
  • Does a .map() that groups sibling elements use <Fragment key={...}> (imported explicitly), never the <> shorthand? See the AI Bug Catalog, entry 15.
  • Does an editor/detail component use key={currentItemId} to force a clean reset when its subject changes, instead of accumulating stale internal state? See Rendering & Reconciliation.
  • Could this state be derived during render instead of synced from a prop with a useEffect? See useEffect & Effects.
  • Is setState ever called directly inside the render body, outside an event handler or effect? See the AI Bug Catalog, entry 17.
  • Is every object/array state update a genuinely new reference, never a .push/direct-assignment mutation followed by calling the setter with the same reference? See State Snapshot & Purity.
  • Is a Context’s provider value memoized (useMemo), and does it carry only slow-changing, broadly-shared data — never a value that changes on every keystroke? See Context.
  • Does every set call after an await inside startTransition(async () => {...}) sit inside its own nested startTransition? See Transitions.
  • Does a data fetch inside an effect cancel its previous in-flight request (AbortController) when its identifier changes quickly? See useEffect & Effects.
  • Is any Date.now()/Math.random() read during render, in code that will ever hydrate — instead of only inside an effect or passed down as a fixed prop? See the AI Bug Catalog, entry 22.
  • Does a useLayoutEffect read a value only a sibling useEffect writes, expecting to see the current commit’s write instead of the previous one? See Commit Phase & Effects.
  • Does every prop passed to a memo-wrapped component stay a stable reference — no inline object/array/function literal built at the call site? See Memoization & the Compiler.
  • If the project claims to run the React Compiler, is babel-plugin-react-compiler actually wired into the build — or does the code just assume auto-memoization that isn’t running? See The React Compiler.
  • Does a component’s return type annotated JSX.Element/ReactElement ever need to return null? Should it be ReactNode instead? See TypeScript with React.
Why does 'is a Context's provider value memoized' belong in the 'State ownership' group rather than 'Memo pairing and compiler assumptions'?
Which checklist group catches this, and how?
useLayoutEffect(() => {
  if (boxRef.current) setMeasuredWidth(parseInt(boxRef.current.style.width, 10));
});
useEffect(() => {
  if (boxRef.current) boxRef.current.style.width = expand ? '200px' : '100px';
}, [expand]);
Why does the checklist ask specifically whether a .map() grouping siblings uses <Fragment key={...}> (imported explicitly), rather than just 'does every list item have a key'?
Which checklist group covers this diff, and what does it ask?
export default function ProductPage({ id }) {
  function handleAddToCart() { console.log('added', id); }
  return <AddToCartButton onAdd={handleAddToCart} />;
}
The checklist says to check whether 'Date.now()/Math.random() is read during render, in code that will ever hydrate.' Why does the 'will ever hydrate' qualifier matter?