React useRef — DOM Access, Mutable Values, and Previous State

Master useRef for DOM manipulation, storing mutable values, and tracking previous state in React. Covers focus management, scroll control, timers, and integration with third-party libraries.

Ref Hooks

Detailed Explanation

useRef for DOM Access and Mutable Values

useRef returns a mutable ref object that persists across renders without causing re-renders when changed.

DOM Element Access

function SearchInput() {
  const inputRef = useRef<HTMLInputElement>(null);

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

  return <input ref={inputRef} placeholder="Search..." />;
}

Storing Mutable Values

Unlike state, changing a ref does not trigger a re-render. This makes refs ideal for values that need to persist but should not cause visual updates:

function Timer() {
  const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);

  const start = () => {
    intervalRef.current = setInterval(() => {
      console.log("tick");
    }, 1000);
  };

  const stop = () => {
    if (intervalRef.current) clearInterval(intervalRef.current);
  };

  return (
    <>
      <button onClick={start}>Start</button>
      <button onClick={stop}>Stop</button>
    </>
  );
}

Tracking Previous Values

A common custom hook pattern uses useRef to remember the previous value of a prop or state:

function usePrevious<T>(value: T): T | undefined {
  const ref = useRef<T | undefined>(undefined);

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

  return ref.current;
}

Integration with Third-Party Libraries

Refs are essential when integrating imperative third-party libraries (e.g., D3, MapboxGL, video players) that need direct DOM access:

function MapComponent() {
  const containerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!containerRef.current) return;
    const map = new mapboxgl.Map({
      container: containerRef.current,
    });
    return () => map.remove();
  }, []);

  return <div ref={containerRef} style={{ width: "100%", height: 400 }} />;
}

Key Rules

  • Never read or write ref.current during rendering (except initialization) -- it breaks concurrent features
  • Do not use refs for values that should cause a re-render -- use state instead

Use Case

Use useRef for imperative DOM operations (focus, scroll, measure), storing timer/interval IDs, holding references to third-party library instances, and tracking values that persist across renders without triggering re-renders.

Try It — React Hooks Reference

Open full tool