useRef Is React's Imperative Escape Hatch for DOM and Persistent Values
Misusing useState for non-UI data causes unnecessary re-renders and tangled effect logic. Knowing exactly when to reach for useRef keeps components lean and avoids the performance tax of rendering for values the user never sees.
React's declarative model abstracts away direct DOM manipulation to avoid expensive cross-engine communication, but some tasks — focus, scroll, media playback — still require a real DOM node. useRef provides that bridge: create a ref, attach it to a JSX element, and access the live DOM inside useEffect after mounting. The same mechanism stores any value that must survive re-renders without causing them, such as timer IDs, previous values, or Web Worker instances. Because JavaScript resets local variables on every function call, useRef wraps its value in an object; the object reference stays stable while `.current` mutates freely. This separation of concerns — state for the UI, refs for the background — keeps rendering predictable and performant.
useRef is less a feature and more a direct consequence of JavaScript's reference-type semantics — the `.current` wrapper isn't a design flourish, it's the only way to cheat the function re-execution model.
The mental model of 'state for the user, refs for the machine' clarifies most Hook decisions and prevents the common mistake of stuffing everything into useState.
Placing side-effectful instantiation like `new Worker()` inside useEffect rather than at the top level is a leak-prevention pattern that applies far beyond Workers — any external resource that should live exactly once per component instance follows the same rule.