Mounting, updating and unmounting, the class lifecycle methods and their hook equivalents, render versus commit, stale closures and correct data fetching.
12 questions2 fresher, 6 mid-level, 4 senior
Interviewers still ask about the class lifecycle, partly because plenty of production code still contains classes, and partly because the names give them a shared vocabulary for asking about timing. You need to be able to talk in both dialects: name the class method, then say what the hooks equivalent is and why it is not always a one to one mapping.
The deeper questions in this section are about when React does what: render versus commit, why effects run after paint, and why a callback can hold a value that is already out of date.
1.What are the phases of a React component's lifecycle?
Fresher
Mounting, when the component is added to the DOM; updating, when props or state change; and unmounting, when it is removed. React also has an error phase for components that catch errors from their children.
Mounting. The component is created, rendered for the first time and inserted into the DOM.
Updating. New props arrive or state changes, React re-renders and applies the difference.
Unmounting. The component is removed, and anything it set up must be torn down.
Error handling. A descendant throws during render, and an error boundary above it renders a fallback.
In a function component you do not name these phases. One useEffect with a cleanup covers setup on mount, re-synchronisation on update and teardown on unmount, which is deliberate: the three used to be written in three places and drift apart.
2.What are the main lifecycle methods in a class component?
Fresher
constructor, render and componentDidMount for mounting; shouldComponentUpdate, render and componentDidUpdate for updating; componentWillUnmount for unmounting; plus getDerivedStateFromError and componentDidCatch for errors.
3.How do you replicate componentDidMount, componentDidUpdate and componentWillUnmount with hooks?
Mid-level
One useEffect covers all three: an empty dependency array is mount, a populated array is update, and the returned cleanup function is unmount. The mapping is not exact, because effects synchronise rather than fire on lifecycle events.
// componentDidMount + componentWillUnmount
useEffect(() => {
const socket = connect(roomId);
return () => socket.close();
}, []);
// componentDidUpdate for a specific value
useEffect(() => {
document.title = `${unread} unread`;
}, [unread]);
Likely follow-up questions
How would you run an effect on update but not on mount?
4.How do you run an effect only when a value changes, and not on the first render?
Mid-level
Track the first render with a ref and return early. There is no built-in option, and needing this often means the logic belongs in an event handler instead.
Worth adding in an interview: if you are skipping the mount run because the effect is really "the user changed the filter", put the call in the change handler. You know there that a user action happened, which an effect can only infer.
Likely follow-up questions
When is an effect the wrong place for this logic?
5.Why does my useEffect run twice when the component mounts?
Mid-level
Because React 18's StrictMode mounts, unmounts and remounts every component in development to check that your effects clean up after themselves. It does not happen in production.
The double invocation is a test, not a bug. An effect that survives it unchanged is an effect that will survive React reusing state in future features, and it is an effect that does not leak a subscription every time it re-runs.
Make the effect cleanable rather than removing StrictMode.
6.What is the difference between the render phase and the commit phase?
Senior
In the render phase React calls your components and builds the new tree; this work can be paused, restarted or thrown away, so it must be pure. In the commit phase React applies the changes to the DOM and runs refs and effects, and that cannot be interrupted.
Render phase. Your component functions run, useMemo calculations run, the new element tree is diffed. No DOM is touched. With concurrent rendering React may run this twice or abandon it.
Commit phase. React applies DOM mutations, then runs useLayoutEffect synchronously, then the browser paints, then useEffect runs.
This is why side effects during render are forbidden. A network request, a mutation of a module level variable or a document write in the render phase can happen twice or happen for a render that is never committed.
Likely follow-up questions
What is React Fiber?
What can be interrupted and what cannot?
7.When a parent re-renders, do all of its children re-render?
Mid-level
By default yes: React re-renders the whole subtree. Re-rendering means calling the component function and diffing, not touching the DOM, so it is usually cheap. React.memo skips a child whose props are unchanged.
Candidates often overstate the cost here. React re-running a function and comparing two small object trees is fast. The DOM is only touched where the diff found a difference, which is the point of the virtual DOM.
Children passed as elements are not re-created by the parent's own state change.
// Slow.js is re-rendered whenever Parent's state changes
function Parent() {
const [n, setN] = useState(0);
return <><button onClick={() => setN(n + 1)}>{n}</button><Slow /></>;
}
// Passing it in as children keeps the same element object, so React bails out
function Parent({ children }) {
const [n, setN] = useState(0);
return <><button onClick={() => setN(n + 1)}>{n}</button>{children}</>;
}
Likely follow-up questions
What is React.memo and when does it not help?
Why is composition sometimes better than memoisation?
8.What is a stale closure, and how do you avoid it?
Senior
A stale closure is a callback that captured props or state from an old render and keeps reading those values after they have changed. It happens when a function outlives the render that created it, typically inside an interval, a subscription or an event listener registered once.
The interval sees count from the first render, forever.
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
setCount(count + 1); // count is always 0
}, 1000);
return () => clearInterval(id);
}, []); // no dependency on count
The three fixes
Functional update.setCount((previous) => previous + 1) needs no captured value at all. Best when you only need the latest state.
Add the dependency. Let the effect tear down and re-run with fresh values. Correct, but restarts the interval every second here.
A ref holding the latest value. Keep a useRef updated in an effect and read ref.current inside the callback. Use this when the callback must be stable and still see current data.
Likely follow-up questions
Why does the dependency array cause this rather than prevent it?
What is the useEffectEvent proposal for?
9.What is the correct way to fetch data in a React component?
Mid-level
In practice, with a data library such as TanStack Query, RTK Query or the framework's loader. If you must do it by hand, use an effect keyed on the request inputs, handle loading and error states, and cancel the request in the cleanup.
10.What is getDerivedStateFromProps, and why is deriving state from props discouraged?
Senior
It is a static class lifecycle that returns a state update from incoming props before every render. It is discouraged because it duplicates the source of truth, and almost every use has a simpler solution: compute during render, or reset with a key.
It replaced componentWillReceiveProps and was deliberately made static so it cannot read this and cannot cause side effects. Even so, it is the API most often used to build the same bug: two copies of one value that drift apart.
If the value can be computed from props, compute it in render, no state needed.
If the component should start over when a prop changes, give it a different key and let React remount it.
If you truly need to remember the previous prop to compare against, store the previous value in state alongside the derived value, which is the pattern the React docs describe.
Likely follow-up questions
What is the hooks equivalent?
How does the key reset trick work?
11.What is shouldComponentUpdate, and what is PureComponent?
Mid-level
shouldComponentUpdate lets a class return false to skip a re-render. PureComponent implements it for you with a shallow comparison of props and state. React.memo is the function component equivalent.
12.In what order do parent and child render functions and effects run?
Senior
Render runs top down: parent first, then children. Effects run bottom up: children's effects fire before the parent's, because a parent's effect may depend on its children having mounted.
Cleanup follows the same bottom up order on unmount. Knowing this matters when a parent measures a child in a layout effect: the child's DOM exists by then, which is exactly why layout effects run after the whole subtree has committed.