- React
- Live Machine Coding Challenges and Custom Hooks
- Machine Coding: usePrevious & useIsMounted Hooks
Machine Coding: usePrevious & useIsMounted Hooks
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';23// 1. usePrevious Hook4export function usePrevious<T>(value: T): T | undefined {5 const ref = useRef<T | undefined>(undefined);67 // 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]);1213 // Returns the value held BEFORE this render's effect executed14 return ref.current;15}1617// 2. useIsMounted Hook18export function useIsMounted(): () => boolean {19 const isMountedRef = useRef(false);2021 useEffect(() => {22 isMountedRef.current = true;23 return () => {24 isMountedRef.current = false;25 };26 }, []);2728 return useCallback(() => isMountedRef.current, []);29}
Deep Architectural Explanation
Why usePrevious Works: The Two-Phase Model
- Render Phase:
- React calls
usePrevious(value). - The function reads
ref.currentand immediately returns it. - At this moment,
ref.currentstill contains the value saved during the previous render'suseEffect. - On the very first render,
ref.currentisundefined.
- React calls
- Commit Phase:
- React mounts/updates the DOM.
- React executes passive effects (
useEffect). - The effect callback runs:
ref.current = value. - Now
ref.currentis 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 must ref.current be updated inside useEffect instead of during render?
Component rendering must be a pure calculation without side-effects. Updating refs during render breaks Concurrent React where renders can be aborted or paused.
Does mutating a ref cause a re-render?
No. useRef creates a plain mutable JavaScript container object whose current property changes without notifying React or scheduling re-renders.
Why should useIsMounted return a callback wrapped in useCallback?
To ensure referential stability so the isMounted function can be passed into useEffect dependencies or child components without triggering unnecessary re-renders.
Can you use usePrevious to compare props changes?
Yes, this is a primary use case: const prevProp = usePrevious(prop); if (prevProp !== prop) { /* do something */ }.
Does React provide a built-in usePrevious hook?
No, the React team intentionally did not add usePrevious to core React, encouraging developers to express logic through declarative effects and state instead.
