跪拜 Guibai
← All articles
JavaScript

useRef Is React's Imperative Escape Hatch for DOM and Persistent Values

By 嘟嘟0717 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

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.

Summary

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.

Takeaways
useRef returns a plain `{ current }` object; mutating `.current` never triggers a re-render.
Bind a ref to a DOM element with the `ref` attribute, then access the real node inside useEffect after mount.
Store Web Worker instances, interval IDs, and previous values in refs to avoid re-render overhead.
useState is reactive and drives the UI; useRef is non-reactive and holds background data.
Refs persist across renders because the object reference is stable, unlike local variables that reset on every function call.
Conclusions

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.

Concepts & terms
Imperative vs. Declarative DOM
Imperative code tells the browser step-by-step what to do (e.g., `document.querySelector`). Declarative code describes the desired end state, and the framework computes the steps. React's declarative model batches and diffs DOM updates to minimize expensive cross-engine communication.
Web Worker
A browser API that runs JavaScript on a background thread, separate from the main UI thread. Heavy computation in a Worker prevents the page from freezing, but the Worker instance itself is a long-lived object best held in a ref.
Reference-type persistence
In JavaScript, primitive values are copied and reset on each function call, but objects are passed by reference. useRef returns the same object across renders, so mutating its `.current` property persists the value without re-rendering.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗