HuntExams Academy logo
ReactIntermediate#lifecycle#hooks

How does the component lifecycle map to hooks?

Answer

componentDidMount maps to useEffect with [], componentDidUpdate to useEffect with dependencies, and componentWillUnmount to the cleanup function returned from useEffect.

Example

useEffect(() => {
  subscribe();            // mount
  return () => unsubscribe(); // unmount
}, []);
useEffect(() => sync(id), [id]); // update

Detailed explanation

Class lifecycle methods describe moments in a component’s lifetime: mounting, updating, and unmounting. Hooks approach this differently: an Effect synchronises a component with an external system, such as a subscription, timer, or browser API. The familiar mapping is componentDidMount to useEffect with [], componentDidUpdate to useEffect with dependencies, and componentWillUnmount to an Effect’s cleanup. This is a useful starting point, but it is not an exact equivalence. Interviewers look for an understanding of when setup and cleanup actually run, rather than just memorised method names.

An Effect with an empty dependency array, useEffect(() => { subscribe(); return () => unsubscribe(); }, []), sets up the subscription after the initial commit and cleans it up when that component instance unmounts. However, in development Strict Mode, React performs an additional setup–cleanup–setup cycle to expose missing cleanup and other bugs. Therefore, describing [] as “runs exactly once” is misleading. It also captures values from the render that created it; it does not automatically read future prop or state values.

An Effect with dependencies, such as useEffect(() => { sync(id); }, [id]), runs after the initial commit and again after commits where id changes according to Object.is. Unlike componentDidUpdate, it does not skip the initial mount. Before rerunning an Effect with changed dependencies, React executes its previous cleanup using the previous render’s captured values. It also runs cleanup on unmount. With no dependency array, the Effect runs after every commit of that component. Include all reactive values used by the Effect instead of suppressing dependency warnings to imitate a lifecycle method.

Under the hood, rendering calculates the UI, while committing applies changes; Effects run only for committed renders, not abandoned render attempts. useEffect generally suits non-layout synchronisation, while useLayoutEffect runs before the browser repaints and is appropriate for layout measurement that must avoid flicker. Neither runs during server rendering. In practice, split Effects by the external process they manage, keep derived values in rendering, and handle user-triggered actions in event handlers. This produces clearer code than translating every lifecycle method into one large Effect.

Key points to remember

  • useEffect with [] resembles mount setup, but development Strict Mode adds a setup–cleanup–setup check.
  • useEffect with dependencies runs initially and after commits where a dependency changes.
  • Cleanup runs before setup repeats with changed dependencies, as well as on unmount.
  • Dependencies use Object.is comparison; include every reactive value referenced by the Effect.
  • Use useLayoutEffect for pre-paint layout work; useEffect handles most external synchronisation.

Common mistakes candidates make

  • Claiming that an Effect with dependencies behaves exactly like componentDidUpdate and skips mounting.
  • Treating cleanup as unmount-only, causing incorrect subscription or resource management.
  • Using [] despite reading changing props or state, creating stale closures and missing synchronisation.

Likely follow-up questions

How would you subscribe to data for a changing id?

Use useEffect(() => { const unsubscribe = subscribe(id); return unsubscribe; }, [id]), assuming subscribe returns a cleanup function. When id changes, React cleans up the old subscription before setting up the new one.

Why should an Effect callback not be async?

An async function returns a Promise, but React expects an Effect callback to return either nothing or a cleanup function. Define and call an async function inside the Effect, and use cleanup to abort supported requests or ignore obsolete results.

Should state derived from props be updated in an Effect?

Usually not: calculate derived values during rendering instead of storing duplicate state and triggering another render. Use useMemo only when caching an expensive calculation is worthwhile, not as a lifecycle replacement.

Interview tip: Start with the familiar lifecycle mapping, then explain initial execution, cleanup before reruns, and the Strict Mode caveat using a subscription example.

Related Interview Questions

1
ReactAdvanced#performance

What is lazy loading and code splitting in React?

Open
2
ReactAdvanced#performance#data

What is Suspense?

Open
3
ReactAdvanced#patterns

What are higher-order components (HOCs)?

Open