- React
- Core React Hooks: State, Synchronization, and Refs
- useRef: DOM Access & Mutable Containers
useRef: DOM Access & Mutable Containers
useRef returns a plain JavaScript object with a persistent { current: value } property that survives across renders without triggering a re-render when mutated. It is used for directly referencing DOM nodes and storing mutable values (timers, previous state) that do not affect the visual UI.
Key Learning Objectives
Differentiate between useRef (persistent mutable container) and useState (render-triggering snapshot).
Explain how React sets ref.current to DOM elements during the commit phase and clears them to null on unmount.
Use useRef to store interval IDs, websocket instances, and previous prop snapshots.
Understand why reading or writing ref.current during rendering is an anti-pattern in Concurrent React.
The Interview Problem & Context
What is logged to the console when mutating ref.current across renders compared to updating component state?
1// Simulating useRef vs useState mechanics2function simulateRefBehavior() {3 let renderCount = 0;4 const countState = 0;5 const countRef = { current: 0 };67 // Mutating ref does NOT trigger a re-render8 countRef.current = 5;9 const rendersAfterRefMutation = renderCount; // Still 01011 // State update schedules re-render (renderCount becomes 1)12 const isSameRefObjectAcrossRenders = true;1314 return `${rendersAfterRefMutation} ${countRef.current} ${countState} ${isSameRefObjectAcrossRenders}`;15}1617console.log(simulateRefBehavior());
Predict Console & Render Output
0 5 0 true
1 5 5 true
0 0 0 false
1 5 0 false
Execution Trace
Step 1 of 3 (Line 4)Initializes state and persistent ref object container.
Deep Architectural Explanation
The Two Core Use Cases of useRef
1. Referencing DOM Elements
When you pass a ref to a JSX element (<input ref={inputRef} />):
- During the Render Phase,
inputRef.currentis stillnull. - During the Commit Phase, after the DOM node is created, React sets
inputRef.current = HTMLInputElement. - When the element unmounts, React sets
inputRef.current = null.
This allows imperative actions:inputRef.current.focus(),scrollIntoView(), or measuring element bounding boxes.
2. Persistent Mutable Container
useRef is essentially equivalent to:
// Conceptual implementation of useRef
function useRef(initialValue) {
const [ref] = useState({ current: initialValue });
return ref;
}It gives you an "instance variable" that survives across all renders. It is ideal for:
- Storing timer IDs (
setInterval) - Storing previous props or state values
- Storing WebSocket or audio stream instances
The Purity Rule: Never Read or Write Refs During Render!
In Concurrent React:
function Bad() {
const countRef = useRef(0);
countRef.current++; // ❌ ANTI-PATTERN: Mutating ref during render!
return <h1>{countRef.current}</h1>; // ❌ Reading ref during render!
}Because render functions may be called multiple times or aborted, mutating refs during render leads to unpredictable, non-deterministic values. Only read and write refs inside effects or event handlers.
Common Interview Traps & Pitfalls
Reading or writing ref.current during rendering (inside the component body or JSX expression).
Using state to hold timer IDs or animation frame references, causing unnecessary re-renders whenever a timer is set.
Expecting ref updates to immediately trigger a UI re-render.
Senior Interviewer Follow-Up Probes
How does React 19 change the way refs are passed to custom components compared to forwardRef in React 18?
How do you handle multiple refs dynamically in a loop using callback refs?
Why does React set DOM refs during the commit phase rather than the render phase?
Frequently Asked Interview Questions
Why shouldn't you read or write ref.current during rendering?
Component rendering must be a pure function. In Concurrent React, renders can be paused, restarted, or discarded; mutating refs during render breaks idempotency and causes unpredictable UI.
How do callback refs work and when should you use them?
Instead of passing a ref object, you pass a function: ref={node => { ... }}. React calls this function with the DOM node on mount and null on unmount, which is ideal for dynamic lists of refs.
When does React attach DOM nodes to ref.current?
During the commit phase, after React has created the DOM nodes and before running useLayoutEffect or useEffect.
Can useRef be used to store the previous value of a prop or state?
Yes. By updating ref.current = value inside a useEffect, ref.current will hold the previous render's value during the next render execution.
