useRef is a box, not just a DOM reference

It is documented alongside DOM access and its more interesting property is that it is a mutable container whose identity survives every render and whose changes do not cause one.

// the DOM case, which everyone knows
const input = useRef(null);
<input ref={input} />

// the other case: a value that must persist but must not render
const timer = useRef();
const renders = useRef(0);
renders.current++;

// and the classic: reading the previous value of a prop
const prev = useRef(id);
useEffect(() => { prev.current = id; }, [id]);

A ref is the right tool exactly when a value needs to survive renders and must not trigger one — timer handles, subscription objects, a flag saying whether the component is still mounted. Using it for anything that should appear on screen is a bug, because nothing re-renders when it changes. The previous-value pattern is worth learning as a named idiom, since it replaces componentDidUpdate‘s prevProps and there is no other way to get it.