Persist

useRef()

const ref = useRef(initialValue)

A "mutable box" that holds a value. Changing it does not trigger a re-render. It's also used to access DOM elements directly.

useRef Hook

A hook that creates a mutable reference object. It persists for the full lifetime of the component.

State vs Ref

Updating State triggers a re-render. Updating a Ref does not. It's perfect for values that don't need to be shown on screen immediately.

Accessing the DOM

The most common use case: accessing a DOM element directly. Like document.getElementById(), but the React way.

The Silent Update

Try incrementing the Ref. Nothing happens on screen! Now force a re-render. Suddenly, the new value appears.

Preserving Values

Use `useRef` to store values like timers, previous props, or any mutable data that shouldn't cause a re-render.

TimerComponent.tsx
function Timer() {
  const [count, setCount] = useState(0);
  
  // 💾 Ref stores the interval ID
  // We need it to clear the timer, but updating it shouldn't render.
  const intervalRef = useRef(null);

  const start = () => {
    intervalRef.current = setInterval(() => {
      setCount(c => c + 1);
    }, 1000);
  };

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

  return (
    <>
      <h1>{count}</h1>
      <button onClick={start}>Start</button>
      <button onClick={stop}>Stop</button>
    </>
  );
}
Storing mutable data

Focusing Inputs

Pass the ref to a React element accessing the DOM node.

InputFocus.tsx
function TextInput() {
  const inputRef = useRef(null);

  const onClick = () => {
    // Access the DOM node directly
    inputRef.current.focus();
    inputRef.current.style.border = "2px solid blue";
  };

  return (
    <>
      <input ref={inputRef} />
      <button onClick={onClick}>Focus Input</button>
    </>
  );
}
Imperative DOM updates

Don't Read During Render

Never read or write `ref.current` during rendering. React assumes your render output is pure. Only use refs in event handlers or effects.

BadCode.tsx
function Bad() {
  const count = useRef(0);

  // ❌ WRONG: Writing to ref during render
  count.current = count.current + 1; 

  // ❌ WRONG: Reading ref during render (might be stale)
  return <div>{count.current}</div>; 
}
Avoid side effects in render
AlgoAnimator: Interactive Data Structures