Cheatsheet
React Deep Dive — Interview Handbook
Condensed for last-minute review — every key takeaway, decision table, and recall card.
01 — FOUNDATION
JSX, Elements & the Virtual DOM
- Key Insight: The Virtual DOM is not a copy of the real DOM—it's a lightweight abstraction. Reconciliation uses heuristics (like key props) to minimize the number of nodes that need to be recreated. Without keys, React may unnecessarily unmount and remount components, causing performance issues and lost state.
- What does JSX transpile to?
- — JSX transpiles to React.createElement(type, props, ...children) calls.
- What is the Virtual DOM?
- — A lightweight JavaScript representation of the real DOM used by React to batch and optimize updates.
- Why should you avoid using array index as a key?
- — Index keys can cause bugs when items are reordered, filtered, or have unstable identities, leading to unnecessary re-renders or lost state.
- What problem do React Fragments solve?
- — Fragments allow grouping multiple elements without adding an extra wrapper node to the DOM.
- When would you use a Portal?
- — Portals are used to render children into a different DOM subtree, such as modals, tooltips, or dropdowns that need to escape overflow or z-index constraints.
02 — CORE
Reconciliation & Fiber Architecture
- ⚠ Killer Trap: Child Component Inside Parent: Defining a child component INSIDE a parent creates new function ref every render → React sees different 'type' → unmount+remount each time → state resets!
| Condition | Action | Effect |
|---|---|---|
| Element type changed (e.g., <div> → <span>) | Unmount + Remount | State LOST · child tree destroyed · effects cleaned up |
| Same type, props unchanged | Skip update | Reuse existing instance, no re-render needed |
| Same type, props changed | Update props | Same instance, state preserved, effects may re-run |
- What does React do when element type changes during reconciliation?
- — Unmounts the old component and remounts a new one. State is lost, child tree destroyed, effects cleaned up.
- What happens if props change but element type stays the same?
- — React updates the props on the same instance. State is preserved, effects may re-run.
- Why is defining a component inside another component problematic?
- — It creates a new function reference every render, causing React to see a different 'type' and unmount/remount each time, resetting state.
- What is the purpose of the key prop in lists?
- — It helps React track elements in lists. Stable IDs (not array index) avoid unnecessary remounts and preserve state.
03 — CORE HOOKS
Core Hooks: useState & useReducer
- Key Insight: Functional Updates Prevent Stale Closures: Always use functional updates when the new state depends on the previous state. This avoids bugs from stale closures, especially in event handlers or effects that capture a snapshot of state.
| Criteria | useState | useReducer |
|---|---|---|
| State complexity | Simple, independent values | Complex objects or multiple sub-values |
| Update logic | Direct or functional updates | Dispatching actions with a reducer |
| Number of state variables | Few (1-3) | Many or nested |
| Testability | Harder to test | Reducer is a pure function, easy to test |
| Performance | Good for small state | Better for large state with many updates |
- What is state batching in React 18+?
- — React groups multiple state updates within the same synchronous block (e.g., event handler, timeout, promise) into a single re-render for performance.
- When should you use useReducer instead of useState?
- — When state logic is complex (multiple sub-values), the next state depends on the previous in intricate ways, or you want to centralize state transitions with a reducer.
- How does the Immer pattern help with useReducer?
- — Immer's produce function lets you write mutable-looking code inside the reducer, automatically creating immutable state updates without manual spreading.
- Why are functional updates important in useState?
- — They ensure the setter uses the latest state value, preventing stale closures and bugs when multiple updates are batched or queued.
04 — CORE HOOKS
Side Effects: useEffect & useLayoutEffect
- Interview Tip: Be prepared to explain the difference between
useEffectanduseLayoutEffectwith a concrete example, such as measuring DOM elements. Also, demonstrate how to handle race conditions in async effects using a cleanup flag orAbortController.
- What is the purpose of the dependency array in useEffect?
- — It tells React when to re-run the effect: only when the listed values change. An empty array runs once on mount; omitting it runs after every render.
- When does the cleanup function in useEffect run?
- — It runs before the component unmounts and before re-running the effect on subsequent renders, allowing you to cancel subscriptions or timers.
- What is the main difference between useEffect and useLayoutEffect?
- — useEffect runs after the browser paints (asynchronous), while useLayoutEffect runs before paint (synchronous), making it suitable for DOM measurements.
- How can you cancel a fetch request in useEffect?
- — Use AbortController: create a controller, pass its signal to fetch, and call controller.abort() in the cleanup function.
05 — CORE HOOKS
Performance Hooks: useMemo & useCallback
- Interview Tip: Interviewers often ask: 'When would you use useCallback vs useMemo?' The key distinction: useCallback returns a memoized function (for referential stability), while useMemo returns a memoized value (for expensive computations). Both depend on dependency arrays. Also, be ready to discuss that premature memoization can harm performance due to memory overhead and dependency comparison costs.
- What is the primary purpose of useCallback?
- — To return a memoized function reference that only changes when its dependencies change, ensuring referential equality across renders.
- When should you NOT use useMemo?
- — When the computation is cheap, dependencies change on every render, or the component is simple and re-renders are not a bottleneck. Premature optimization can add overhead without benefit.
- What is the anti-pattern with React.memo and callbacks?
- — Wrapping a component in React.memo but passing an inline function as a prop. Since inline functions create new references each render, the memoization becomes ineffective.
- What does referential equality mean in React?
- — React uses strict equality (===) to compare props and dependencies. Two objects or functions are considered equal only if they reference the same memory location, not if they have the same content.
06 — PATTERNS
List Performance & Virtualisation
- Interview Tip: Always start with virtualization — it's the single biggest performance gain. Then layer on memoization and stable keys. Only reach for Web Workers if you're doing heavy computation.
| Step | Technique | Impact |
|---|---|---|
| 1 | Virtualization (react-window, TanStack Virtual) | Biggest win — render only visible rows |
| 2 | Pagination / Infinite Scroll (50-100 rows, IntersectionObserver) | Great UX — load more on demand |
| 3 | React.memo + Stable Refs (wrap Row component, stable props) | Incremental — avoid re-renders |
| 4 | useMemo for Sort/Filter (avoid recompute on parent re-render) | Incremental — memoize expensive ops |
| 5 | Stable Keys (unique IDs, never array index) | Correctness — prevent broken state |
| 6 | CSS Containment (contain: layout style paint) | Browser opt — reduce per-row work |
| 7 | Defer Updates (useDeferredValue, startTransition) | Advanced — keep input responsive |
| 8 | Web Worker (heavy sort/filter offloaded) | Last resort — non-blocking UI |
- What is the single biggest performance win for rendering 10K rows?
- — Virtualization — render only the visible rows using libraries like react-window or TanStack Virtual.
- Why should you never use array index as a key for dynamic lists?
- — Array indices are unstable when items are added, removed, or reordered. This causes incorrect state, broken animations, and unnecessary re-renders.
- What does CSS containment (contain: layout style paint) do for list performance?
- — It tells the browser to isolate each row's layout, style, and paint calculations, reducing the work needed when a single row changes.
- How do useDeferredValue and startTransition help with large lists?
- — They allow you to defer non-urgent updates (like filtering a 10K list) so the UI stays responsive to user input like typing.
07 — PATTERNS
Context API & State Management
- Re-render Pitfall: Every time the context value changes (e.g., a new object reference), all consumers re-render. This can cause performance problems in large trees. Always memoize the context value with
useMemoto avoid unnecessary re-renders when the underlying data hasn't changed.
| Feature | Context + useReducer | Zustand | Redux |
|---|---|---|---|
| Boilerplate | Low | Low | High |
| Performance | Can cause re-renders | Selective subscriptions | Selective subscriptions |
| Middleware | Manual | Built-in | Rich ecosystem |
| DevTools | Limited | Supported | Excellent |
| Best for | Small to medium apps | Medium to large apps | Large, complex apps |
- What is prop drilling?
- — Prop drilling is the process of passing props through multiple intermediate components that don't need the data themselves, just to reach a deeply nested child component.
- How does Context API solve prop drilling?
- — Context API allows you to provide data at a high level in the component tree and consume it directly in any descendant component without manually passing props through every level.
- What is the re-render scope of a Context Provider?
- — When the value of a Context Provider changes, all components that consume that context will re-render, regardless of whether they use the changed part of the value.
- What is the Context + useReducer pattern?
- — It combines React's Context API with the useReducer hook to manage complex state. The reducer handles state transitions, and the context provides both state and dispatch to the component tree, offering a predictable state management pattern similar to Redux.
- When should you use Zustand or Redux instead of Context?
- — Use Zustand or Redux when you have deeply nested updates, frequent state changes, need selective subscriptions for performance, require middleware or devtools, or are building a large, complex application where Context's re-render behavior becomes a bottleneck.
08 — PATTERNS
Custom Hooks & Composition
- Key Insight: Custom hooks are not just for reusing logic—they also enable composition. You can combine multiple hooks inside a single custom hook to create more complex behaviors. For example, a
useSearchhook could internally use bothuseDebounceanduseFetchto debounce a search query and fetch results.
- What are the two main rules of hooks?
- — 1. Only call hooks at the top level of your component or custom hook. 2. Only call hooks from React function components or custom hooks.
- What is the purpose of the useDebounce hook?
- — It delays updating a value until after a specified delay, commonly used to limit the rate of API calls from search inputs.
- How do you test a custom hook in isolation?
- — Use the renderHook function from @testing-library/react-hooks to test the hook's behavior without a component wrapper.
- What does the useLocalStorage hook typically return?
- — It returns an array with the current stored value and a setter function that updates both the state and localStorage.
10 — ADVANCED
Server Components & Suspense
- Interview Tip: Be ready to explain how Server Components reduce bundle size and improve performance. A common question: 'How do you decide when to use a Server Component vs a Client Component?' Answer: Use Server Components for static data and logic that doesn't need interactivity; use Client Components for interactive UI with hooks, event handlers, or browser APIs.
- What is the difference between React Server Components (RSC) and Client Components (RCC)?
- — RSC run on the server, reducing client-side JavaScript, and cannot use hooks or browser APIs. RCC run in the browser, can use hooks and event handlers, and are marked with 'use client'.
- How does Suspense work with async data fetching?
- — Suspense allows components to 'suspend' rendering while waiting for an async operation (like data fetching) to complete. It shows a fallback UI until the data is ready.
- What is the use() hook and how does it relate to Suspense?
- — The use() hook reads a promise directly within a component, suspending rendering until the promise resolves. It integrates with Suspense boundaries to provide a declarative loading state.
- What is a common mistake when migrating to Server Components?
- — Forgetting to add 'use client' to components that use hooks or event handlers, causing errors because Server Components cannot use client-side features.
09 — ADVANCED
Testing React Components
- Key Insight: Always prefer
userEventoverfireEventfor simulating user interactions. It's more realistic and helps catch bugs that only appear with full event sequences. Install it withnpm install --save-dev @testing-library/user-event.
| Method | Returns | Throws if not found | Use case |
|---|---|---|---|
| getBy* | element | Yes | Element must exist synchronously |
| queryBy* | element or null | No | Assert element is absent |
| findBy* | Promise<element> | Yes (after timeout) | Element appears after async update |
- What is the core philosophy of React Testing Library?
- — Test behavior, not implementation. Write tests that simulate user interactions and verify outcomes, rather than testing internal state or lifecycle methods.
- When should you use findBy* instead of getBy*?
- — When the element appears asynchronously (e.g., after data fetching or state updates). findBy* returns a promise that resolves when the element is found.
- What is the main advantage of userEvent over fireEvent?
- — userEvent simulates full, realistic user interactions (e.g., typing triggers focus, keydown, keyup, change) rather than dispatching a single event, catching more edge cases.
- How do you test a component that depends on React Context?
- — Wrap the component in a test provider that supplies a controlled value, often using a custom render function like renderWithTheme.
- What is a major pitfall of snapshot tests?
- — They are fragile and break on trivial changes (whitespace, class order), making them hard to review and giving a false sense of coverage. Prefer targeted assertions.