Module 10: Interview Questions
Core Concepts: Questions and Answers
The fundamentals interviewers ask about most, with the kind of answer that shows you understand the mechanism and not just the vocabulary.
A quick word on how to use this lesson. Don't memorize the answers. Interviewers can tell when you're reciting. Read each question, try to answer it out loud first, then compare. The answers below are written the way I'd say them in an interview: a direct first sentence, then the reasoning behind it. Aim for that shape. Lead with the point, then show you know why.
What is React, and what problem does it solve?#
React is a JavaScript library for building user interfaces out of components. The problem it solves is keeping the screen in sync with your data as that data changes.
With plain DOM code you update every affected element by hand each time something changes, and bugs creep in when you miss one. In React you describe what the UI should look like for the current state, and React figures out which DOM changes are needed. The short version is that the UI is a function of state.
What is JSX? Do you have to use it?#
JSX is a syntax extension that lets you write markup-like code inside JavaScript. It isn't valid JavaScript, so a build tool compiles it into function calls (jsx("h1", { children: "Hi" })) that return plain objects called React elements.
You don't have to use it. You can call createElement or jsx by hand and get the same result. Nobody does because it's unreadable beyond trivial cases.
What is the virtual DOM, and how does reconciliation work?#
The "virtual DOM" is a description of the UI, a tree of plain JavaScript objects (React elements) that your components return. When state changes, React calls your components again to get a new tree, compares it with the previous tree, and applies only the differences to the real DOM. That comparison is reconciliation.
It uses two heuristics to stay fast. Elements of different types produce different trees, so React tears down the old subtree and builds a new one. And for lists, it uses key props to match items between renders.
One thing I'd add to show depth: the virtual DOM isn't what makes React fast compared with hand-written DOM code. What it gives you is a way to write declarative code without paying for rebuilding the entire page on every change.
Why do lists need keys?#
Keys give each item in a list a stable identity, so React can tell whether an item was added, removed or moved. Without them, React compares by position, which can reuse the wrong component instance when items are reordered or removed. The result is bugs like an input's text appearing in the wrong row.
A good key is unique among siblings and stays the same for the same item across renders, typically an id from your data. Using the array index is only safe if the list is static and never reordered or filtered.
What's the difference between props and state?#
Props are inputs passed from a parent. They're read-only inside the component. State is data a component owns and can change over time, and changing it triggers a re-render.
A useful way to say it: props are like function arguments, state is like a variable that survives between calls. If a value can be computed from props or other state, it shouldn't be state at all.
Why can't you mutate state directly?#
React decides whether something changed by comparing references. If you mutate an object or array and pass the same reference to the setter, React sees "same value" and may skip the re-render, so the UI goes stale. It also breaks memoization, because components that were skipped because the reference was unchanged won't see the update, and it makes bugs very hard to trace since the value changed behind everyone's back.
So you create a new object or array (spread, map, filter) and set that.
Is setState synchronous? What is batching?#
No. Calling the setter schedules an update. The variable you're holding doesn't change, because each render has its own snapshot of state. React collects the updates made during an event handler and processes them together in one render, which is batching. Since React 18 this happens everywhere, including inside promises and timeouts.
That's why setCount(count + 1) three times in a row only adds one, and why you should use the updater form setCount((c) => c + 1) when the next value depends on the previous one.
Controlled vs uncontrolled components?#
A controlled input has its value held in React state: you pass value and update it in onChange. React is the single source of truth. An uncontrolled input keeps its own value in the DOM, and you read it when needed with a ref or FormData, and use defaultValue for an initial value.
Controlled is better when you need live validation, formatting or to affect other UI as the user types. Uncontrolled is simpler and cheaper for large forms where you only need the values on submit. Libraries like React Hook Form lean on uncontrolled inputs for performance.
What does "lifting state up" mean?#
When two components need the same data, you move the state to their closest common ancestor and pass it down as props, along with callbacks to change it. It keeps a single source of truth. The opposite principle matters too: don't lift state higher than necessary, because it makes unrelated components re-render and couples distant parts of your app.
Composition vs inheritance: which does React favor?#
Composition. Instead of building class hierarchies, you combine components, mostly through props and children. A Dialog component can accept any content as children, and a DeleteButton is a Button with preset props. It's more flexible than inheritance and avoids tangled hierarchies. React's own docs say they haven't found a case where inheritance is the better choice for components.
What is prop drilling, and how do you avoid it?#
It's passing a prop through several layers of components that don't use it, only to reach a deep one. A couple of levels is fine, since it's explicit. To reduce it, you can restructure with children or by passing elements as props, move state closer to where it's used, use context for widely needed, rarely changing values, or use a state library for complex global state.
Why must components be pure? What does StrictMode do?#
A component's render should be a pure calculation: given the same props, state and context, it returns the same output and doesn't change anything outside itself. React relies on that to be able to render components more than once, pause and resume renders, or skip them, without visible side effects.
In development, StrictMode helps you check this by rendering components twice and re-running effects with an extra setup and cleanup. If your component behaves differently on the second run, it has a side effect in the wrong place, or an effect without proper cleanup. It does nothing in production.
When does a component re-render?#
When its own state changes, when its parent re-renders, or when a context it consumes changes. Notably, a change in props isn't a separate trigger. The child re-renders because the parent did. A re-render means React calls your function again, which isn't the same as updating the DOM. If the output is identical, nothing touches the DOM.
What's the difference between a React element and a component?#
An element is a plain object describing what to render: { type: "div", props: {...} }. It's cheap, immutable and the output of JSX. A component is a function (or class) that takes props and returns elements. <Card /> creates an element whose type is the Card function, and React calls that function to find out what it renders.
What are Fragments and why would you use them?#
A component has to return a single root. A fragment (<>...</>) lets you group siblings without adding an extra DOM node, which matters for valid HTML (tr children inside a table) and for layouts that depend on direct children, like flexbox and grid. When you need a key on a fragment in a list, use <Fragment key={...}>.
How do events work in React?#
You attach handlers with camelCase props like onClick. React wraps the native event in a synthetic event with a consistent API across browsers. Under the hood, React attaches a single listener per event type at the root container and dispatches to your handlers based on the event's target, which is event delegation. Events bubble through the React tree as expected, and stopPropagation and preventDefault work as in the DOM.
What is an error boundary?#
A component that catches errors thrown while rendering its children, in lifecycle methods and in constructors below it, and shows a fallback instead of unmounting the whole app. Currently they have to be class components, using getDerivedStateFromError and componentDidCatch, though the react-error-boundary package wraps this for you. They don't catch errors in event handlers, in async code like timeouts and promises, or errors in the boundary itself.
What changed in React 19?#
I'd mention the things that affect everyday code. ref is now a regular prop on function components, so forwardRef is no longer needed. You can render a context directly as a provider (<ThemeContext value={...}>). Form actions let you pass a function to a form's action prop, with useActionState and useFormStatus for pending and result state. The use function can read promises and context, including conditionally. There's built-in support for rendering title and meta tags from components. And the React Compiler, which reached a stable release as a separate tool, handles memoization automatically.
What would you say is the most common mistake you see in React code?#
This one is an open-ended question where they want to see your experience. A solid answer: overusing useEffect. People use effects to derive state from props, to respond to events, or to copy data into state, all of which are better handled by computing values during render or doing the work in an event handler. The second would be storing derived data in state and fighting to keep it in sync.
Practice#
- Pick five questions from above and answer each one out loud in under a minute, without looking. Record yourself if you can stand it, and listen back for rambling.
- For each answer you stumbled on, write a three-line code example that demonstrates the concept.
- Ask a friend to interrupt you in the middle of an answer with "why?" and see whether you can keep going one level deeper. That's what a good interviewer does.