React Curriculum

Machine Coding: usePrevious & useIsMounted Hooks

Easy100% Free~20 mins#react#machine-coding#custom-hooks#usePrevious#useIsMounted#useref
Interview Answer (30-Second Summary)

usePrevious leverages a mutable useRef to return the value from the preceding render pass before updating the ref in useEffect. useIsMounted returns a boolean callback indicating whether the component is currently mounted, preventing state updates after unmount.

Key Learning Objectives
✓

Implement usePrevious using useRef and useEffect execution timing.

✓

Explain why usePrevious returns the old value during render and updates in the commit phase.

✓

Implement useIsMounted to prevent async memory leaks on unmounted components.

✓

Contrast mutable container refs with component state.

The Interview Problem & Context

Implement `usePrevious<T>(value: T): T | undefined` and `useIsMounted(): () => boolean` using `useRef` and explain why ref updates occur inside `useEffect`.

1import { useRef, useEffect, useCallback } from 'react';
2
3// 1. usePrevious Hook
4export function usePrevious<T>(value: T): T | undefined {
5 const ref = useRef<T | undefined>(undefined);
6
7 // useEffect runs AFTER the render phase commit has completed!
8 // Therefore, during the current render, ref.current still holds the PREVIOUS render's value!
9 useEffect(() => {
10 ref.current = value;
11 }, [value]);
12
13 // Returns the value held BEFORE this render's effect executed
14 return ref.current;
15}
16
17// 2. useIsMounted Hook
18export function useIsMounted(): () => boolean {
19 const isMountedRef = useRef(false);
20
21 useEffect(() => {
22 isMountedRef.current = true;
23 return () => {
24 isMountedRef.current = false;
25 };
26 }, []);
27
28 return useCallback(() => isMountedRef.current, []);
29}
Deep Architectural Explanation

Why usePrevious Works: The Two-Phase Model

  1. Render Phase:
    • React calls usePrevious(value).
    • The function reads ref.current and immediately returns it.
    • At this moment, ref.current still contains the value saved during the previous render's useEffect.
    • On the very first render, ref.current is undefined.
  2. Commit Phase:
    • React mounts/updates the DOM.
    • React executes passive effects (useEffect).
    • The effect callback runs: ref.current = value.
    • Now ref.current is primed with the new value for the next render!

Why Not Mutate the Ref During Render?

Mutating a ref during render (ref.current = value in the function body) violates the Rules of React!
If React pauses or aborts a concurrent render pass, the ref would be prematurely overwritten with draft state that was never committed to the screen.

Common Interview Traps & Pitfalls
✕

Updating `ref.current = value` during render instead of inside `useEffect`, causing it to always return the current value.

✕

Expecting ref changes to trigger component re-renders.

✕

Using `useState` to store previous values, causing an infinite loop of re-renders.

Senior Interviewer Follow-Up Probes
?

How does React 19 Action pending state eliminate the need for manual mount checking?

?

How can usePrevious be adapted to track a history of the last N values?

?

What happens if a component renders twice without committing (e.g. concurrent rendering)?

Frequently Asked Interview Questions
Why does usePrevious return undefined on the initial render?

Because on mount, the useEffect updating ref.current has not run yet. The initial ref value is undefined.

Component rendering must be a pure calculation without side-effects. Updating refs during render breaks Concurrent React where renders can be aborted or paused.

No. useRef creates a plain mutable JavaScript container object whose current property changes without notifying React or scheduling re-renders.

To ensure referential stability so the isMounted function can be passed into useEffect dependencies or child components without triggering unnecessary re-renders.

Yes, this is a primary use case: const prevProp = usePrevious(prop); if (prevProp !== prop) { /* do something */ }.

No, the React team intentionally did not add usePrevious to core React, encouraging developers to express logic through declarative effects and state instead.