AcademyReact
Refs and Imperative DOM Access
useRef is a mutable box that survives re-renders without causing them — the escape hatch for the DOM and for values that are not part of render.
3 min readUpdated 3 exercises
What useRef actually returns
useRef(initial) returns one object, for the lifetime of the component, shaped exactly like { current: initial }. That is the entire API. The object identity never changes between renders — React hands you back the same object every time — only .current changes, and you change it yourself.
function Example() {
const ref = useRef(0);
// ref is the SAME object every render.
// ref.current can be reassigned freely, any time, without re-rendering.
}The contrast with useState that makes it click
useState: setting it schedules a re-render. Reading it inside render always
gets the value as of the render currently running.
useRef: setting .current does nothing to the render cycle. Reading it
gets whatever was last assigned, synchronously, right now.State is for anything the UI needs to reflect. A ref is for anything a component needs to remember that has no business being on the screen — a timer id, a previous value for comparison, whether an effect already ran, a scroll position you're about to restore.
Attaching a ref to a DOM node
The most common ref: pass it as the ref prop on a host element, and after that element mounts, .current holds the actual DOM node.
function AutoFocusInput() {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus(); // imperative call — no prop does this
}, []);
return <input ref={inputRef} />;
}There is no declarative prop for "focus this input" the way there is for value or className, because focus is an imperative action on a specific DOM node at a specific moment, not a description of what the UI should look like. That is the shape of every legitimate ref-to-DOM use case: measuring an element's size, scrolling it into view, calling .play() on a video, reading .files off a file input. All of them are "do a thing to this exact node right now," not "render this."
Refs as general state-without-rerender
The DOM is the most visible use, but a ref is just as often used for a plain value:
function StopwatchButton() {
const intervalId = useRef(null);
function start() {
intervalId.current = setInterval(tick, 1000);
}
function stop() {
clearInterval(intervalId.current);
}
// storing the interval id in state would re-render the button on every
// start/stop for no visual reason — a ref holds it for free
}usePrevious is the canonical example of this pattern, and worth knowing by construction because it comes up constantly once you see it:
function usePrevious(value) {
const ref = useRef(undefined);
useEffect(() => {
ref.current = value; // runs AFTER this render commits
});
return ref.current; // still holds the value from the PREVIOUS render
}The effect body runs after the DOM update for the render that just happened, so during that render, ref.current still holds whatever the previous effect wrote — i.e. the previous render's value. Read it and update it in the same effect, in that order, and you get a one-render-behind value for free.
Why writing to a ref during render is a bug
// BAD
function BadComponent({ value }) {
const ref = useRef(0);
ref.current = value; // mutating during render
return <p>{ref.current}</p>;
}Render is supposed to be a pure computation of "given these props and this state, what does the UI look like" — React may call a component function more than once per commit (Strict Mode double-invokes render in development specifically to catch this), and a mutation sitting directly in the function body runs every one of those extra times too, silently corrupting a value nothing asked it to touch. The fix is always the same: read and write refs from an event handler or an effect, where "this runs exactly when I think it runs" is actually true.
Now practice it
1 challenge · 2 quizzesReading this page is the cheap half. These are the exercises that make you use it.
- usePrevious HookProImplements usePrevious exactly as described above — the timing between the ref read and the ref write is the entire exercise.ChallengeSolve · EasyChallengeSolve · Easy
- Hooks · Custom HooksCustom-hook questions that lean on knowing when a value belongs in a ref instead of triggering a re-render.QuizTake · MediumQuizTake · Medium
- Hooks · Core HooksCore hook questions, including when a value belongs in a ref instead of in state.QuizTake · EasyQuizTake · Easy
Read next
Dependencies and Cleanup in useEffect
An Effect synchronizes with something outside React. The dependency array says what it reads, not when to run — get that backwards and you get stale values.
4 min
Writing Custom Hooks
A custom hook is a plain function that calls other hooks. It shares logic, not state — two components using it get two independent copies.
5 min