codejump

AcademyReact

Refs and Imperative DOM Access

useRef is a mutable box that survives re-renders without causing them — the escape hatch for the DOM and for values that are not part of render.

3 min readUpdated

3 exercises

Waiting at the end of this page.

What useRef actually returns

useRef(initial) returns one object, for the lifetime of the component, shaped exactly like { current: initial }. That is the entire API. The object identity never changes between renders — React hands you back the same object every time — only .current changes, and you change it yourself.

JSX
function Example() {
  const ref = useRef(0);
  // ref is the SAME object every render.
  // ref.current can be reassigned freely, any time, without re-rendering.
}

The contrast with useState that makes it click

useState: setting it schedules a re-render. Reading it inside render always
          gets the value as of the render currently running.
useRef:   setting .current does nothing to the render cycle. Reading it
          gets whatever was last assigned, synchronously, right now.

State is for anything the UI needs to reflect. A ref is for anything a component needs to remember that has no business being on the screen — a timer id, a previous value for comparison, whether an effect already ran, a scroll position you're about to restore.

Attaching a ref to a DOM node

The most common ref: pass it as the ref prop on a host element, and after that element mounts, .current holds the actual DOM node.

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

  useEffect(() => {
    inputRef.current.focus(); // imperative call — no prop does this
  }, []);

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

There is no declarative prop for "focus this input" the way there is for value or className, because focus is an imperative action on a specific DOM node at a specific moment, not a description of what the UI should look like. That is the shape of every legitimate ref-to-DOM use case: measuring an element's size, scrolling it into view, calling .play() on a video, reading .files off a file input. All of them are "do a thing to this exact node right now," not "render this."

Refs as general state-without-rerender

The DOM is the most visible use, but a ref is just as often used for a plain value:

JSX
function StopwatchButton() {
  const intervalId = useRef(null);

  function start() {
    intervalId.current = setInterval(tick, 1000);
  }
  function stop() {
    clearInterval(intervalId.current);
  }
  // storing the interval id in state would re-render the button on every
  // start/stop for no visual reason — a ref holds it for free
}

usePrevious is the canonical example of this pattern, and worth knowing by construction because it comes up constantly once you see it:

JSX
function usePrevious(value) {
  const ref = useRef(undefined);
  useEffect(() => {
    ref.current = value; // runs AFTER this render commits
  });
  return ref.current; // still holds the value from the PREVIOUS render
}

The effect body runs after the DOM update for the render that just happened, so during that render, ref.current still holds whatever the previous effect wrote — i.e. the previous render's value. Read it and update it in the same effect, in that order, and you get a one-render-behind value for free.

Why writing to a ref during render is a bug

JSX
// BAD
function BadComponent({ value }) {
  const ref = useRef(0);
  ref.current = value; // mutating during render
  return <p>{ref.current}</p>;
}

Render is supposed to be a pure computation of "given these props and this state, what does the UI look like" — React may call a component function more than once per commit (Strict Mode double-invokes render in development specifically to catch this), and a mutation sitting directly in the function body runs every one of those extra times too, silently corrupting a value nothing asked it to touch. The fix is always the same: read and write refs from an event handler or an effect, where "this runs exactly when I think it runs" is actually true.

Now practice it

1 challenge · 2 quizzes

Reading this page is the cheap half. These are the exercises that make you use it.