useRef

A Box for Values That Survive Renders Without Triggering Them

useRef

`useRef` gives you a `{ current }` object that React preserves across renders. Two main uses — DOM refs and instance variables.

5 min read Level 2/5 #react#useRef#refs
What you'll learn
  • Hold a DOM node in a ref
  • Hold a mutable value that doesn't cause re-renders
  • Tell refs apart from state

useRef(initial) returns an object { current: initial }. React holds onto that object across renders — you can read or write .current whenever, and the value persists. Crucially: changing .current does NOT trigger a re-render.

Two Things People Use Refs For

1. DOM nodes

You saw this in the previous chapter:

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

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

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

Passing the ref to the JSX ref prop tells React to assign the DOM node into inputRef.current after mount.

2. Instance Variables

For values that should survive renders, but where updating them doesn’t change the UI.

function StopWatch() {
  const intervalIdRef = useRef(null);
  const [seconds, setSeconds] = useState(0);

  function start() {
    intervalIdRef.current = setInterval(() => {
      setSeconds(s => s + 1);
    }, 1000);
  }

  function stop() {
    clearInterval(intervalIdRef.current);
  }

  return <>{/* UI ... */}</>;
}

The interval id is needed across renders, but changing it doesn’t affect the UI — perfect for a ref.

Ref vs State

QuestionStateRef
Triggers re-render?YesNo
Should the UI show it?YesNo
Mutated in event handlers?No (replace)Yes (.current = x)
Survives renders?YesYes
Available during render?YesYes (but treat as escape hatch)

If the user should see the value → useState. If the value is internal bookkeeping → useRef.

Don’t Read or Write Refs in the Render Body

It works, but it makes the component impure (render output depends on something outside the props/state cycle). Refs belong inside event handlers and effects.

// ✗ Mutating in render — surprises later
function Bad() {
  const r = useRef(0);
  r.current++;
  return <p>{r.current}</p>;
}

Previous-Value Pattern

A common idiom: track the previous render’s value:

function usePrevious(value) {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
}

The effect updates the ref AFTER render, so the value returned this render is whatever the value was last render.

Up Next

Memoizing expensive computations.

useMemo →