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.
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.styletakes an object. true,false,null,undefinedrender 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. NotMath.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 ParentReal 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.
keychanges 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 withObject.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 →
| Bug | Cause | Fix |
|---|---|---|
| Input text jumps to another row | key={index} after a delete | key={item.id} |
| Typed text lost on parent render | Component defined inside a component | Define at module level |
| Old form state after prop change | Same type, same position | key={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.
| Form | Runs |
|---|---|
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 →
| Trap | Fix |
|---|---|
Stale closure in setInterval | setCount(c => c + 1), honest deps, or useEffectEvent |
| Fetch race (old answer lands last) | ignore flag or AbortController |
| Derived state in an effect | Compute in render (extra render otherwise) |
| Reset state on prop change | key, not an effect |
| Reaction to a click | Event handler |
async effect function | Define async inside, call it |
| Effect sets state it depends on | Infinite 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
refis a prop - useMemo
- Skip a slow calculation. A hint, may be dropped
- useCallback
- Same function identity. Only helps
memochildren and deps - useReducer
- Many updates, rules in one pure fn.
dispatchis 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 →
onChangefires on every keystroke. Events bubble.stopPropagationstops them.preventDefaultstops a submit reload.- A button in a form submits by default: use
type="button"for the rest. - Controlled:
value+onChange, start with""neverundefined. Uncontrolled:defaultValue, read withFormData. - 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()
pendingof 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 →
fetchdoes not reject on 404 or 500. Checkres.okand throw. Aborted fetch rejects withAbortError.- 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,
invalidateQueriesto refetch. Retry defaults to 3. Never copydatainto 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's | useState there |
| Siblings must agree | Closest common parent |
| Computable | Nowhere: compute in render |
| Many fields, many updates | useReducer |
| Many readers, rarely changes | Context |
| Server data | Query cache |
| Shared, changes often | Store with selectors (Zustand, Redux Toolkit) |
| Shareable or survives refresh | The URL |
- Context re-renders every consumer, even
memoones. 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 →
- Measure first: render counter,
Profiler, DevTools. Production build, throttled CPU. - A component renders for 3 reasons: first time, own state or context, parent rendered.
- Cheapest fix: move state down, or pass heavy parts as
children. memo+ stable props (useCallback). One inline function defeatsmemo(15 renders, 0 when fixed).- Long lists: virtualise (50,006 DOM nodes became 115). Heavy code:
lazy+ Suspense. - Slow but unavoidable:
useTransitionoruseDeferredValue. 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 newkey. - Boundaries: root, per route, per widget. Portals move the DOM node only; context and event bubbling follow the React tree.
| Term | Meaning |
|---|---|
| SSR | Server makes first HTML, browser hydrates |
| Server Component | Runs 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, thengetByLabelText,getByText.getByTestIdlast. getBythrows,queryByreturns null (assert absence),findBywaits. UseuserEvent, andawaitevery action.- Mock the edges (network, timers), not your own components.
renderHookfor hooks. Snapshots only when small. - jsdom has no layout: keep a few end-to-end tests.
Interview one-linersLesson 9 →
| Question | Short 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.