Cheat sheet · React in Depth: From Components to Performance

React in Depth cheat sheet

React 19 in one place: components and JSX, the render and commit cycle, state, effects, hooks, forms, data fetching, state placement, performance, patterns and testing. A component is a pure function from props and state to UI.

Read the full series →

blogs · jp-blogs.vercel.app/cheatsheets/series/react

JSX and componentsLesson 0 →

  • JSX is function calls: a tag becomes jsx(X, { a: "1" }), which returns a plain element object.
  • Component names start with a capital. Lowercase makes a DOM tag and your function is never called.
  • Return one root (or a fragment). Close every tag. className, htmlFor, onClick. style takes an object.
  • true, false, null, undefined render nothing. A plain object as a child is an error.
  • Props are read-only and flow down. Events go up: pass a callback prop.

Watch out: {count && ...} renders 0 when count is 0. Write count > 0 && .... Also onClick={f()} calls f during render. Write onClick={f}.

Pure rendering and listsLesson 0 →

  • Render must be pure: same props, state, context give the same JSX, with no changes to outside variables, no fetch, no timers.
  • Strict Mode (dev only) calls components twice to expose impurity. Local mutation of things made in this render is fine.
  • Lists need a key: stable and unique among siblings, ideally a data ID. Not Math.random(). Index only for static lists.
  • Conditional rendering is plain JS: early return, ternary, &&, return null.

Trigger, render, commitLesson 1 →

render Parent, render Child
ref attached
layoutEffect Child
(browser paints)
effect Child, effect Parent

Real order from a traced run. Children's effects run before their parent's.

  • Render calls your functions. Commit touches the DOM, only where it differs. A re-render can change nothing.
  • A parent render re-renders all children, unless the child is wrapped in memo.
  • Same type at the same position keeps state. Different type resets it. key changes identity.

useState rulesLesson 1 →

setCount(count + 1); // x3 gives +1
setCount((c) => c + 1); // x3 gives +3
const [v] = useState(
  () => readStorage()); // lazy
  • State is a snapshot. The setter queues a render. The variable in your handler does not change.
  • Updates are batched into one render, also in timeouts and promises (React 18+).
  • Never mutate. Copy: { ...obj, k }, [...arr, x], arr.filter, arr.map. React compares with Object.is.
  • Do not store what you can compute. Do not copy props into state. Keep state low, lift only when siblings must agree.

Keys and identity bugsLesson 1 →

BugCauseFix
Input text jumps to another rowkey={index} after a deletekey={item.id}
Typed text lost on parent renderComponent defined inside a componentDefine at module level
Old form state after prop changeSame type, same positionkey={userId}

useEffectLesson 2 →

useEffect(() => {
  const c = connect(roomId);
  return () => c.disconnect(); // cleanup
}, [roomId]);

Old cleanup runs first, then the new setup. Unmount runs the last cleanup.

FormRuns
useEffect(fn, [a, b])After mount and when a or b changes
useEffect(fn, [])Mount only (cleanup on unmount)
useEffect(fn)After every render
  • Effects sync with an outside system. They are not "on mount" or "on update" hooks.
  • Strict Mode runs setup, cleanup, setup in dev. Write the cleanup. Never "fix" it with a ref flag.
  • Object or function dependencies are new each render, so the effect re-runs. Use primitives.

Effect traps and fixesLesson 2 →

TrapFix
Stale closure in setIntervalsetCount(c => c + 1), honest deps, or useEffectEvent
Fetch race (old answer lands last)ignore flag or AbortController
Derived state in an effectCompute in render (extra render otherwise)
Reset state on prop changekey, not an effect
Reaction to a clickEvent handler
async effect functionDefine async inside, call it
Effect sets state it depends onInfinite loop: restructure

Watch out: Never silence exhaustive-deps. useLayoutEffect runs before paint: only to measure the DOM.

Hooks cheat listLesson 3 →

useRef
Box that does not render. DOM nodes, timer IDs. In 19 ref is a prop
useMemo
Skip a slow calculation. A hint, may be dropped
useCallback
Same function identity. Only helps memo children and deps
useReducer
Many updates, rules in one pure fn. dispatch is stable
useSyncExternalStore
Subscribe to a non-React store safely
use (19)
Read a promise or context. Callable in if. Promise must be stable
useId
Unique, hydration-safe ID for labels
  • Rules: top level only, never in conditions, loops, handlers or after an early return. React matches hooks by call order.
  • A custom hook (name starts with use) shares logic, not state. Each call has its own.

Events and formsLesson 4 →

  • onChange fires on every keystroke. Events bubble. stopPropagation stops them. preventDefault stops a submit reload.
  • A button in a form submits by default: use type="button" for the rest.
  • Controlled: value + onChange, start with "" never undefined. Uncontrolled: defaultValue, read with FormData.
  • Validate on the server too. Labels with htmlFor, aria-invalid, aria-describedby, role="alert".
form action={fn}
Calls fn(formData) in a transition
useActionState(fn, init)
[state, formAction, isPending]
useFormStatus()
pending of the parent form (call in a child)
useOptimistic
Temporary value until the real state arrives

Watch out: React 19 resets uncontrolled fields after an action. Return typed values in state and use them as defaultValue.

Fetching server dataLesson 5 →

  • fetch does not reject on 404 or 500. Check res.ok and throw. Aborted fetch rejects with AbortError.
  • Effect fetching has races, no sharing, no cache, waterfalls. Use TanStack Query, SWR, RTK Query or a framework loader.
  • Query key holds everything the function reads: ["user", id]. staleTime (default 0) is how long data is fresh. gcTime (5 min) is how long unused data is kept.
  • After a mutation, invalidateQueries to refetch. Retry defaults to 3. Never copy data into state.
  • use(promise) + Suspense: promise must be created outside render. Suspense does not remove waterfalls.

Where state livesLesson 6 →

State is…Put it in
One component'suseState there
Siblings must agreeClosest common parent
ComputableNowhere: compute in render
Many fields, many updatesuseReducer
Many readers, rarely changesContext
Server dataQuery cache
Shared, changes oftenStore with selectors (Zustand, Redux Toolkit)
Shareable or survives refreshThe URL
  • Context re-renders every consumer, even memo ones. Split contexts, memoise the value, put state and dispatch apart.
  • Zustand: selector returning a new object each time can loop. Pick narrow slices.

PerformanceLesson 7 →

  1. Measure first: render counter, Profiler, DevTools. Production build, throttled CPU.
  2. A component renders for 3 reasons: first time, own state or context, parent rendered.
  3. Cheapest fix: move state down, or pass heavy parts as children.
  4. memo + stable props (useCallback). One inline function defeats memo (15 renders, 0 when fixed).
  5. Long lists: virtualise (50,006 DOM nodes became 115). Heavy code: lazy + Suspense.
  6. Slow but unavoidable: useTransition or useDeferredValue. Fix the render if it takes 2 s.

Tip: React Compiler adds memoization at build time (15 renders to 0). Components and hooks only. Still learn memo and useMemo.

Patterns, boundaries, portalsLesson 8 →

  • Composition: children, slots (JSX props), compound components, render props, custom hooks. Avoid 25 boolean props.
  • Error boundary catches render errors (and errors in startTransition). Not event handlers, timers or promises. Reset with a new key.
  • Boundaries: root, per route, per widget. Portals move the DOM node only; context and event bubbling follow the React tree.
TermMeaning
SSRServer makes first HTML, browser hydrates
Server ComponentRuns only on server, code never shipped
'use client'Marks the client boundary, not "no SSR"
'use server'Marks Server Functions, not Server Components

Testing ReactLesson 8 →

  • Test like a user: render, getByRole (with name) first, then getByLabelText, getByText. getByTestId last.
  • getBy throws, queryBy returns null (assert absence), findBy waits. Use userEvent, and await every action.
  • Mock the edges (network, timers), not your own components. renderHook for hooks. Snapshots only when small.
  • jsdom has no layout: keep a few end-to-end tests.

Interview one-linersLesson 9 →

QuestionShort answer
Is the virtual DOM why React is fast?No. It saves manual updates; speed comes from fewer renders.
setState three times?Adds 1. Use the updater form.
Why keys?Identity, so state follows the item.
Effects run twice?Strict Mode in dev. Fix cleanup.
When does a component re-render?New, own state or context, or parent rendered.
Does memo always work?No: inline function props and context defeat it.
Server Components vs SSR?Different. SSR hydrates. RSC code never ships.
Context vs store?Context carries a value. Stores give selectors.

A cheat sheet is a reminder, not a teacher. Each card links to the lesson that explains it step by step.