A Box for Values That Survive Renders Without Triggering Them
useRef
`useRef` gives you a `{ current }` object that React preserves across renders. Two main uses — DOM refs and instance variables.
What you'll learn
- Hold a DOM node in a ref
- Hold a mutable value that doesn't cause re-renders
- Tell refs apart from state
useRef(initial) returns an object { current: initial }. React
holds onto that object across renders — you can read or write
.current whenever, and the value persists. Crucially: changing
.current does NOT trigger a re-render.
Two Things People Use Refs For
1. DOM nodes
You saw this in the previous chapter:
function AutoFocus() {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus();
}, []);
return <input ref={inputRef} />;
} Passing the ref to the JSX ref prop tells React to assign the DOM
node into inputRef.current after mount.
2. Instance Variables
For values that should survive renders, but where updating them doesn’t change the UI.
function StopWatch() {
const intervalIdRef = useRef(null);
const [seconds, setSeconds] = useState(0);
function start() {
intervalIdRef.current = setInterval(() => {
setSeconds(s => s + 1);
}, 1000);
}
function stop() {
clearInterval(intervalIdRef.current);
}
return <>{/* UI ... */}</>;
} The interval id is needed across renders, but changing it doesn’t affect the UI — perfect for a ref.
Ref vs State
| Question | State | Ref |
|---|---|---|
| Triggers re-render? | Yes | No |
| Should the UI show it? | Yes | No |
| Mutated in event handlers? | No (replace) | Yes (.current = x) |
| Survives renders? | Yes | Yes |
| Available during render? | Yes | Yes (but treat as escape hatch) |
If the user should see the value → useState. If the value is
internal bookkeeping → useRef.
Don’t Read or Write Refs in the Render Body
It works, but it makes the component impure (render output depends on something outside the props/state cycle). Refs belong inside event handlers and effects.
// ✗ Mutating in render — surprises later
function Bad() {
const r = useRef(0);
r.current++;
return <p>{r.current}</p>;
} Previous-Value Pattern
A common idiom: track the previous render’s value:
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
} The effect updates the ref AFTER render, so the value returned this render is whatever the value was last render.
Up Next
Memoizing expensive computations.
useMemo →