Skip to content

React useRef Interview Guide

useRef is a React Hook that creates a mutable reference object whose value persists across re-renders.

const inputRef = useRef(null);

It returns:

{
current: value
}

Unlike state, updating ref.current does not trigger a re-render.

flowchart TD
    A[Component renders] --> B[useRef creates object once]
    B --> C["{ current: initialValue }"]
    C --> D[Subsequent renders reuse same object]
    D --> E[Updating current does not trigger re-render]
  • Accessing DOM elements
  • Storing previous values
  • Storing mutable values without re-rendering
  • Managing timers (setTimeout / setInterval)
  • Integrating third-party libraries

Example:

const inputRef = useRef();
useEffect(() => {
inputRef.current.focus();
}, []);
useRef useState
Doesn’t trigger re-render Triggers re-render
Mutable Immutable updates
Stores reference Stores UI state
Faster for non-UI values Used for UI updates
const countRef = useRef(0);
countRef.current++;

UI won’t update.

const [count, setCount] = useState(0);
setCount(count + 1);

UI updates immediately.

Why Updating ref.current Doesn’t Re-render

Section titled “Why Updating ref.current Doesn’t Re-render”

React tracks state changes, not changes to properties on a ref object.

ref.current = 100;

The object identity remains the same, so React does not schedule a render.

const myRef = useRef(0);

The same ref object is returned on every render, preserving:

myRef.current
function App() {
const inputRef = useRef();
const focusInput = () => {
inputRef.current.focus();
};
return (
<>
<input ref={inputRef} />
<button onClick={focusInput}>Focus</button>
</>
);
}
const [count, setCount] = useState(0);
const prevCount = useRef();
useEffect(() => {
prevCount.current = count;
}, [count]);

Usage:

Current: {count}
Previous: {prevCount.current}
createRef useRef
Used in Class Components Used in Functional Components
New object each render Same object every render
Less efficient in function components Preferred
const ref = createRef(); // new object every render
const ref = useRef(); // persistent object
const renderCount = useRef(0);
renderCount.current++;

Useful for caching values, timers, API request tracking, and counters.

const timerRef = useRef();
useEffect(() => {
timerRef.current = setInterval(() => {
console.log("Running");
}, 1000);
return () => clearInterval(timerRef.current);
}, []);
const ref = useRef(10);
ref.current === 10

The initial value is assigned only once.

No, not for UI state.

Bad:

const countRef = useRef(0);
countRef.current++;

Good:

const [count, setCount] = useState(0);

Use state whenever UI updates are required.

Allows a parent to pass a ref to a child component.

const Input = React.forwardRef((props, ref) => {
return <input ref={ref} />;
});

Parent:

const inputRef = useRef();
<Input ref={inputRef} />
const Input = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus() {
inputRef.current.focus();
}
}));
return <input ref={inputRef} />;
});

Parent:

inputRef.current.focus();

Problem:

setTimeout(() => {
console.log(count);
}, 5000);

Solution:

const countRef = useRef(count);
useEffect(() => {
countRef.current = count;
}, [count]);
setTimeout(() => {
console.log(countRef.current);
}, 5000);

For mousemove, scroll, drag-and-drop, or WebSocket messages:

Using state:

setPosition(...);

causes many re-renders.

Using refs:

positionRef.current = value;

avoids rendering overhead.

flowchart LR
    A[Render] --> B[State updated?]
    B -->|Yes| C[React re-renders]
    B -->|No| D[Ref object reused]
    D --> E[current may change]
    E --> F[No render triggered]
const ref = useRef();

The object identity survives every render.

Hook Purpose
useMemo Cache expensive computed values
useRef Store mutable persistent values
const value = useMemo(() => expensiveCalc(), [dep]);
const cache = useRef();

Refs can contribute to leaks if they retain timers, event listeners, DOM nodes, or large objects without cleanup.

useEffect(() => {
const timer = setInterval(() => {}, 1000);
return () => clearInterval(timer);
}, []);
const websocketRef = useRef();
websocketRef.current = new WebSocket(url);

Useful for:

  • WebSocket connections
  • WebRTC
  • Media players
  • Maps SDKs
  • Payment SDKs
  • Analytics tracking
Hook Purpose
useState Store UI state
useEffect Side effects
useMemo Cache expensive calculations
useCallback Cache function references
useRef Store mutable values without re-render
  • Why use useRef instead of useState?
  • How do you prevent stale closures using refs?
  • How do forwardRef and useImperativeHandle work together?
  • How would you optimize a component receiving thousands of events per second using useRef?
  • useRef stores mutable values that persist across renders.
  • Updating ref.current does not trigger a re-render.
  • Use refs for DOM access, timers, caches, previous values, and external object references.
  • Use state for UI updates and refs for non-visual mutable data.
  • Mastering forwardRef and useImperativeHandle is important for advanced React interviews.