useRef — Remembering Without Re-rendering
When you click Add book, the cursor should jump straight into the title field. React describes the page with JSX, but focusing an input means talking to the actual DOM element. You need a way to hold on to it. That is one of two jobs for a ref.
The rule
A ref is a box that remembers but never re-renders.
import { useRef } from 'react';
function AddBookForm() {
const titleRef = useRef(null);
function handleAddClick() {
titleRef.current.focus();
}
return (
<>
<input ref={titleRef} placeholder="Title" />
<button onClick={handleAddClick}>Add book</button>
</>
);
}useRef(initial) returns an object with one property, current. React gives you the same object on every render, so whatever you put in current stays there.
Job one — DOM access. Pass the ref to an element with ref={titleRef}. After React commits, titleRef.current is the real <input> node, so you can call focus(), scrollIntoView(), or measure its size. Before it is attached, current is null.
Job two — hidden memory. Any value the component must remember but does not show — an interval id, the previous value of a prop, a flag — can live in ref.current:
const intervalRef = useRef(null);
function start() {
intervalRef.current = setInterval(tick, 1000);
}
function stop() {
clearInterval(intervalRef.current);
}Never re-renders. Changing ref.current does not tell React anything. That is its point — and its trap. If you store a displayed number in a ref, the screen will not update.
Two usage rules: read and write ref.current in event handlers and effects, not during rendering, because a ref is outside the pure render. And to pass a ref to your own component, React 19 lets you accept ref as a normal prop; older code wraps the component in forwardRef.
The shortcut
For every value a component remembers, ask: "Does the screen show it?" Yes → state. No → ref.
The number of likes
It appears on the button. State.
Drills
Answer fast — trust the rule.
What happens when you set countRef.current = 5?
What is inputRef.current after <input ref={inputRef} /> has been committed?
A counter shown on screen is stored in a ref and incremented on click. What do you see?
Where should you call titleRef.current.focus()?
Remember
- A ref is a box that remembers but never re-renders.
- Does the screen show it? Yes → state; no → ref.
- Trap: storing displayed values in refs, and touching refs during render.
