React Interview Questions for AI Training Work
AI training platforms often test React directly, through a live coding round or a technical screen, rather than just taking a resume's word for it. These questions cover the parts of React that actually come up under that kind of scrutiny: Hooks & Component Lifecycle, State Management & Re-renders and Performance Optimization.
Below are 10 questions split into technical, scenario, and behavioral rounds, each with a full written answer so you can see what a strong response sounds like.
Technical (5)
What's the difference between state and props, and why can't a component modify its own props?
Props are read-only data passed down from a parent, while state is data a component owns and can change itself, typically via `useState`. Props are immutable from the child's perspective because React's data flow is one-directional: if a child could mutate its props directly, the parent's copy of that data would go out of sync with what's rendered, breaking the predictability that makes React's rendering model work.
Explain the dependency array in `useEffect` and a common mistake developers make with it.
The dependency array tells React when to re-run the effect: it re-runs whenever any value in the array changes between renders, and runs once on mount if the array is empty. A common mistake is omitting a value the effect actually uses, like a prop or state variable referenced inside, which causes the effect to run with a stale closure over an outdated value instead of re-running when that value changes.
What causes unnecessary re-renders in a React component tree, and how would you diagnose one?
A component re-renders when its state changes, its parent re-renders, or its props change identity, even if the new props are shallowly equal in value, like a new object or array literal created on every render. I'd use the React DevTools Profiler to record a render pass and see which components re-rendered and why, rather than guessing which memoization to add first.
What's the difference between `useMemo` and `useCallback`?
Both cache a value across renders unless a dependency changes, but `useMemo` caches the result of a computation, like an expensive derived value, while `useCallback` caches a function reference itself. `useCallback(fn, deps)` is functionally equivalent to `useMemo(() => fn, deps)`, it exists mainly for readability when the goal is specifically to keep a stable function identity, commonly to avoid re-triggering a memoized child component.
How does React's reconciliation use `key` when rendering a list, and what goes wrong if you use the array index as the key?
React uses `key` to match elements between renders and decide whether to update an existing DOM node or create a new one. Using the array index as a key breaks this when items are reordered, inserted, or removed, because the index no longer reliably identifies the same logical item, which can cause React to reuse the wrong DOM node, showing stale state, like an input's value, attached to the wrong list item.
Scenario (3)
A form component re-renders on every keystroke and the app visibly lags on a slower device. How would you fix it?
I'd first confirm with the Profiler that the lag is actually re-render cost and not something else, like an expensive computation running inside the render itself. If unrelated child components are re-rendering unnecessarily, I'd wrap them in `React.memo` and stabilize the props passed to them with `useMemo`/`useCallback`. If the real cost is the input's own re-render, debouncing the state update or moving that field to uncontrolled with a ref can help.
You need to share state between two sibling components that don't have a direct parent-child relationship. What are your options?
The most direct fix is lifting the state up to their nearest common ancestor and passing it down as props to both. If that ancestor is far up the tree and prop drilling through several layers gets unwieldy, Context is the next option for state that's read in many places, and a dedicated state library becomes worth it once the shared state and its update logic gets complex enough that Context alone starts causing broad re-renders.
How would you handle a component that needs to fetch data on mount and also clean up if the component unmounts before the fetch finishes?
Inside `useEffect`, I'd set up an abort controller or a local `let ignore = false` flag, start the fetch, and check that flag before updating state when the response resolves. The effect's cleanup function sets the flag or calls `abort()`, so if the component unmounts mid-request, the fetch either gets cancelled or its result is simply discarded instead of triggering a state update on an unmounted component.
Behavioral (2)
Tell me about a time you had to debug a stale closure bug in a React component.
I had a `setInterval` set up inside `useEffect` with an empty dependency array that referenced a state variable, and the callback kept using the value from the very first render instead of the current one. The fix was either adding the variable to the dependency array and resetting the interval when it changed, or switching to a functional state update that doesn't depend on the closed-over value at all.
Describe a time you chose not to optimize a component's re-renders even though you could have.
On a settings panel that re-rendered on every keystroke in an unrelated field, I measured the actual render cost with the Profiler and found it was under a millisecond, well below anything a user would notice. Adding `React.memo` and memoized callbacks there would have added code complexity for a performance gain nobody would ever perceive, so I left it as-is and documented why in case someone later assumed it needed fixing.
Knowing the answer and saying it out loud under pressure are different skills.
The Academy has free modules and mock exams to build the second one.
Open roles using React
See all roles →
React Native Engineer
$80-100
React and Next.js Engineer
$70-90