Interview Questions
A curated set of React interview questions with precise, senior-level answers. They span the topics interviewers probe most: rendering internals, Hooks, performance, and state.
Fundamentals
What is the virtual DOM and how does reconciliation work?
The virtual DOM is a lightweight in-memory tree of React elements. On each render React builds a new tree and diffs it against the previous one (reconciliation), computing the minimal set of real DOM mutations and applying only those. The diffing uses heuristics—elements of different types produce different trees, and keys identify which children moved—to stay roughly O(n) instead of a naive O(n³) tree comparison.
Why do list items need a key, and why avoid the index?
Keys give React a stable identity for each item so it can match elements across renders instead of tearing them down. Using the array index ties identity to position, so inserting, removing, or reordering items shifts keys and causes React to associate the wrong state/DOM with the wrong item—producing visual bugs and wasted work. Use a stable ID from your data.
What’s the difference between props and state? Props are read-only inputs passed from a parent; a component cannot change its own props. State is private, mutable memory owned by the component, updated via its setter, and a change triggers a re-render. Props flow down; state lives where it’s owned.
What is one-way data flow? Data flows in a single direction: parent to child via props. Children request changes by invoking callback props the parent passed down—they never mutate parent data directly. This makes the source of any value predictable.
What is JSX?
A syntax extension that compiles to React.createElement calls. It lets you write HTML-like markup in JavaScript; expressions go in { }, attributes are camelCased, and each expression must return a single root.
Function vs. class components—which should I use?
Function components with Hooks, exclusively, in new code. Classes are legacy: more boilerplate, confusing this binding, and no access to newer features. Hooks gave function components full parity and better composition.
Hooks
What are the Rules of Hooks? Call Hooks only at the top level (never in conditions, loops, or nested functions) and only from React functions (components or custom Hooks). React identifies Hooks by call order, so the order must be identical on every render.
useMemo vs useCallback—what’s the difference?
useMemo(fn, deps) memoizes the return value of fn; useCallback(fn, deps) memoizes the function itself. In fact useCallback(fn, deps) equals useMemo(() => fn, deps). Use useMemo for expensive computations or stable object references; use useCallback for stable function references passed to memoized children.
When does useEffect run, and what does the dependency array do?
Effects run after render and paint. The dependency array scopes re-runs: [] runs once on mount, [a, b] re-runs when a or b change, and omitting the array runs after every render. The returned cleanup function runs before the next effect and on unmount.
What’s a common useEffect pitfall?
Stale closures from an incomplete dependency array—the effect captures an old value and misbehaves silently. Also: missing cleanup (leaks/duplicate subscriptions), and using effects for logic that belongs in event handlers or in render-time derivation.
What does useRef do?
Holds a mutable .current value that persists across renders without causing a re-render. Used for DOM node access and for storing instance-like values such as timer IDs or previous values.
useState vs useReducer?
useState suits simple, independent values. useReducer centralizes complex transitions—multiple interdependent sub-values or actions—into a pure reducer, making updates testable and predictable.
Rendering & Performance
What triggers a re-render? A component re-renders when its state changes, when its parent re-renders, or when a consumed context value changes. Re-rendering recomputes JSX; it does not necessarily mutate the DOM—reconciliation decides what actually changes.
What is React.memo?
A higher-order component that skips re-rendering when props are shallowly equal to the previous render. Pair it with useCallback/useMemo so callback and object props keep stable references, otherwise memo is defeated.
How do you fix an expensive child re-rendering on every parent update?
Wrap the child in React.memo, and stabilize the props you pass it with useCallback (functions) and useMemo (objects/arrays). Confirm with the Profiler before and after.
What is code splitting in React?
Loading parts of the bundle on demand with React.lazy and <Suspense>, so users download code only when a route or feature is reached—reducing initial bundle size.
State Management
What does “lifting state up” mean? Moving shared state to the nearest common ancestor of the components that need it, then passing the value and an updater down as props. It keeps siblings in sync from a single source of truth.
What problem does Context solve, and what’s its main caveat? Context shares values (theme, user, locale) across the tree without prop drilling. Caveat: every consumer re-renders when the context value changes, so avoid putting frequently-changing or large state in a single context—split contexts or use a state library.
When do you need a state library like Redux or Zustand? When state is genuinely global, shared across distant parts of the tree, or has complex update logic and middleware needs. For server data, prefer TanStack Query/SWR over a general store—they’re purpose-built for caching and synchronization.
Misc
Controlled vs uncontrolled components?
A controlled input derives its value from state (value + onChange), making React the single source of truth—ideal for validation and dynamic behavior. An uncontrolled input keeps its value in the DOM, read via a ref; simpler for basic forms and required for file inputs.
Why does an effect run twice in development?
<StrictMode> intentionally mounts, unmounts, and remounts components once in development to verify that effects clean up correctly. It’s a development-only check and does not occur in production.
How do you handle errors in the UI?
Use an error boundary—a component that catches render-phase errors in its subtree and shows a fallback. Boundaries are still class components (or a wrapper like react-error-boundary); they don’t catch errors in event handlers or async code, which you handle with try/catch.