The first version of this list did well enough that people asked for more, so here are five more React questions I actually reach for in interviews. Same idea as before: none of these are trivia, they all come from bugs I have watched people ship.
1. Why can using an array index as a key break things?
Most people know "don't use index as key" as a rule without knowing what it protects against. The key tells React which element in the new list matches which element in the old list. If you key by index and the list reorders, index 0 is still index 0, so React thinks nothing moved. It keeps the old component instances in place and just updates their props.
For a list of plain text that is fine. The moment a row holds state that React owns, it breaks: an uncontrolled input keeps the value that belonged to the row that used to be there, focus stays on the wrong row, a half-finished CSS transition plays on the wrong element. Prepending an item is the classic trigger, because every index shifts by one and React re-renders every single row instead of mounting one.
Use a stable id from the data. If you genuinely have no id and the list never reorders, index is acceptable, but say so out loud so the next person knows it was a choice.
2. What is a stale closure and where does it bite in React?
A closure captures the variables that were in scope when the function was created, not when it runs. In React your function components re-run on every render, so each render has its own copy of props and state. A callback created on render 1 closes over render 1's state forever.
useEffect(() => {
const id = setInterval(() => {
setCount(count + 1); // count is always 0 here
}, 1000);
return () => clearInterval(id);
}, []); // empty deps, so this effect only ever saw the first render
The interval callback was created once, when count was 0, and it keeps reading that 0. The fixes are the functional updater setCount(c => c + 1), or putting count in the deps and letting the effect re-subscribe, or a ref if you need the latest value without re-running the effect. The same trap shows up in event handlers passed to addEventListener and in setTimeout callbacks inside effects.
3. Why can't you call a hook inside a condition?
React does not pass hooks a name or a key. It relies purely on call order. On every render, the first useState it sees maps to the first state slot, the second to the second, and so on. If a hook call is behind an if, the order changes between renders, and now useState number 2 is reading the slot that belonged to a useEffect.
That is the whole reason for the rules of hooks. It is not style, it is that the mechanism has no other way to line up "this call" with "that piece of state." Once people see it as an array indexed by call count, the lint rule stops feeling arbitrary.
4. Here is a component wrapped in React.memo that still re-renders every time. Why?
React.memo does a shallow compare of props. If the parent passes an object or array or function literal as a prop, that literal is a brand new reference on every render, so the shallow compare always says "different," and memo does nothing.
<Child style={{ margin: 8 }} onClick={() => doThing(id)} />
Both style and onClick are recreated each render. To make the memo actually hold you have to stabilise those references with useMemo and useCallback, or lift the object out of the component if it never changes, or restructure so the child does not need them. A lot of "I added memo and nothing got faster" reports are exactly this.
5. Will an error boundary catch an error thrown in an onClick handler?
No. Error boundaries catch errors during rendering, in lifecycle methods, and in the constructors of the tree below them. They do not catch errors in event handlers, in async code, in setTimeout, or in the boundary's own code. Event handlers run outside the render cycle, so React lets the error propagate to the normal browser error handling instead.
If you want handler errors to show a fallback, you catch them yourself and put the failure into state, then render based on that state. Knowing this line matters because people wire up a boundary, test it by throwing in a click handler, see nothing happen, and conclude the boundary is broken.
If your answer to any of these was "it's just best practice," that is usually the sign there is a mechanism underneath worth understanding. Happy to hear which ones you would swap out.
Top comments (0)