Volume 3

Q158: useRef, DOM Refs and Mutable Instance Values

Difficulty: SeniorFrequency: HighAnswer time: 8-12 minutes

What Interviewers Want To Evaluate

This question checks whether you understand refs as mutable containers outside the render data flow. Interviewers want DOM refs, mutable values, no re-render on ref change, previous value tracking, timers, imperative APIs, callback refs, and React 19 initial value expectations.

Senior answers should explain when refs are appropriate and when state is required.

Short Interview Answer

useRef returns a stable object whose .current value can be mutated without causing a re-render. It is commonly used for DOM nodes, timers, imperative handles, previous values, and values that need to survive renders without affecting UI. If changing a value should update the screen, use state instead. In React 19 TypeScript patterns, refs should be initialized explicitly, usually with null for DOM refs.

Detailed Interview Answer

useRef creates a stable container.

const inputRef = useRef(null);

React keeps the same ref object across renders.

inputRef.current;

Changing .current does not trigger a render.

DOM Refs

DOM refs let React give you access to a host element.

function SearchBox() {
  const inputRef = useRef(null);

  function focusInput() {
    inputRef.current?.focus();
  }

  return <input ref={inputRef} />;
}

This is useful for focus, selection, measurement, and imperative browser APIs.

Mutable Instance Values

Refs can store mutable values that should survive renders.

const timeoutIdRef = useRef(null);

Common examples:

timer IDs
latest callback
previous values
DOM nodes
external library instances
abort controllers

No Re-Render

This does not update the UI:

countRef.current += 1;

If the screen must reflect the new value, use state.

setCount((count) => count + 1);

Previous Value Pattern

function usePrevious(value) {
  const ref = useRef(undefined);

  useEffect(() => {
    ref.current = value;
  }, [value]);

  return ref.current;
}

The ref stores data across renders without causing another render.

Latest Callback Pattern

Refs can avoid stale callback reads in subscriptions.

const latestOnChange = useRef(onChange);

useEffect(() => {
  latestOnChange.current = onChange;
}, [onChange]);

This should be used carefully. New React patterns like effect events may be clearer where available.

Callback Refs

Callback refs run when a node is attached or detached.

<div ref={(node) => {
  if (node) {
    measure(node);
  }
}} />

They are useful for dynamic lists or immediate node handling.

Imperative APIs

Some third-party widgets require imperative instances.

const chartRef = useRef(null);

Initialize and clean up in effects.

useEffect(() => {
  chartRef.current = createChart(containerRef.current);

  return () => {
    chartRef.current?.destroy();
  };
}, []);

Common Mistakes

A common mistake is using refs to store data that should be rendered.

Another mistake is reading or writing refs during render in ways that make output depend on mutable external state.

Another mistake is forgetting cleanup for imperative instances.

Senior Trade-Offs

Refs are escape hatches. They are right for imperative integration and non-visual mutable values.

State is right for visual data. Props are right for parent-controlled data. Refs are right when mutation should not trigger rendering.

Code or Design Example

function AutoFocusInput() {
  const inputRef = useRef(null);

  useEffect(() => {
    inputRef.current?.focus();
  }, []);

  return <input ref={inputRef} />;
}

This synchronizes React with the browser focus API.

Debugging Workflow

When ref behavior is confusing:

check whether value should be state instead
check initial ref value
check when DOM node is available
check cleanup for timers or widgets
check stale closure workarounds
avoid render output based on mutated refs

Interview Framing

Define refs as stable mutable containers. Then contrast refs with state and show DOM focus or timer examples.

Revision Notes

Refs persist across renders and mutation does not re-render. Use them for imperative or non-visual values.

Key Takeaways

Refs are useful precisely because they sit outside normal render updates. That power should be used deliberately.

Follow-Up Questions

  1. What does useRef return?
  2. Does changing ref.current re-render?
  3. When should state be used instead?
  4. How are DOM refs used?
  5. What are callback refs?
  6. How can refs store timer IDs?
  7. What is a previous value ref?
  8. Why can refs be escape hatches?
  9. What cleanup do imperative instances need?
  10. What changed around initial ref values in React 19 typing?

How I Would Answer This In A Real Interview

I would say useRef gives a stable mutable container. I use it for DOM access, timers, external instances, and values that should persist without re-rendering. If a value affects UI, I use state instead.