useRef
useRef gives you a mutable box — an object {' '} — that persists across renders but does not trigger a re-render when you change it. It has two everyday jobs: holding a reference to a DOM element (to focus an input or measure a node), and remembering a value (like a timer id or a previous value) without affecting the UI.
Learn useRef — DOM Refs & Persisting Values in our free React course — a beginner-friendly interactive lesson with runnable examples, a practice exercise and…
Part of the free React course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
1️⃣ A Ref Is a Persistent Box
useRef(initial) returns an object {' '} . The same object survives every render, and you read or write .current directly — no setter. Crucially, writing to it does not schedule a re-render.
2️⃣ Ref vs State
Both useRef and useState remember a value between renders. The difference is the re-render: updating state tells React to re-render; updating a ref does not. Rule of thumb: if a value should appear on screen, use state; if it's bookkeeping the UI doesn't display, use a ref.
Your turn — build a render counter using a ref's .current :
3️⃣ DOM Refs & Persisting Values
Attach a ref to an element with the ref attribute. After the element mounts, React sets .current to the real DOM node — so you can focus it, scroll it, or measure it. Note it's null during the first render, so read it in an effect or an event handler. Refs are also perfect for remembering a previous value across renders.
These lines focus an input on mount using a ref. Put them in the right order:
📋 Quick Reference
1. You do ref.current++ three times. How many re-renders?
Zero. Changing a ref never triggers a re-render.
2. Why is inputRef.current null on the first render?
The DOM node doesn't exist until after the component mounts. React fills in .current afterward — read it in an effect.
3. A value must appear on screen and update. Ref or state?
State — only state re-renders the UI when it changes.
Use a ref to remember the time of the last click and report the gap between clicks. Run it and check your output.
Practice quiz
What does useRef return?
useRef(initial) returns a mutable object { current: initial } whose identity is stable across renders.
What happens when you change a ref's .current value?
- Nothing re-renders; the value just persists
- The component re-renders
- React throws an error
- All children re-render
Answer: Nothing re-renders; the value just persists. Changing .current does NOT trigger a re-render. The value is remembered across renders, but the UI does not update.
What is the key difference between useRef and useState?
- useRef is faster to read
- useState cannot hold objects
- useRef cannot persist values
- Updating state re-renders; updating a ref does not
Answer: Updating state re-renders; updating a ref does not. Both persist a value across renders, but setState triggers a re-render while writing to a ref's .current does not.
You do ref.current++ three times. How many re-renders occur?
- Three
- Zero
- One
- Two
Answer: Zero. Changing a ref never triggers a re-render, so the count of re-renders is zero.
How do you attach a ref to a DOM element?
- <input ref={inputRef} />
- <input value={inputRef} />
- <input current={inputRef} />
- useRef(<input />)
Answer: <input ref={inputRef} />. Pass the ref to the element's ref attribute: <input ref={inputRef} />. After mount, React sets inputRef.current to the DOM node.
Why is inputRef.current null during the first render?
- Because useRef starts at null always
- Because refs are read-only
- Because the DOM node does not exist until after the component mounts
- Because React clears it on every render
Answer: Because the DOM node does not exist until after the component mounts. The DOM node is only created after mount, so the ref is null during the first render. Read it in an effect or event handler.
A value must appear on screen and update when it changes. Ref or state?
- Ref, because it is faster
- State, because only state re-renders the UI
- Either works the same
- Ref, then call forceUpdate
Answer: State, because only state re-renders the UI. If a value should be visible and update the UI, use state — only state re-renders when it changes. Refs are for non-visual bookkeeping.
Where should you safely read a DOM ref's .current to call .focus()?
- During render
- In the component's return statement
- Before the ref is created
- In an effect or an event handler, after mount
Answer: In an effect or an event handler, after mount. The DOM node exists only after mount, so read the ref in an effect or event handler — not during render, where it is still null.
Which is a good use case for a ref?
- A counter shown on screen
- Storing a timer id (setInterval) or a previous value without re-rendering
- Text typed into a controlled input that displays live
- A list rendered to the page
Answer: Storing a timer id (setInterval) or a previous value without re-rendering. Refs are ideal for non-visual bookkeeping like timer ids or remembering a previous value — things the UI does not display.
You get 'Cannot read properties of null (reading focus)'. What is the fix?
- Use useState instead of useRef
- Initialize the ref with a DOM node
- Read .current in an effect or handler after mount, not during render
- Remove the ref attribute
Answer: Read .current in an effect or handler after mount, not during render. That error means you read .current during render while it was still null. Read it in an effect or handler, after the element mounts.