Understanding the useRef Hook in React


React’s useRef
is a powerful hook that helps manage references to DOM elements and persists mutable values across renders without causing re-renders. Let’s break it down simply.
useRef
useRef is like a secret pocket in your React components—it lets you store things without making your component re-render. Let's break it down with real examples you can actually use.
1. The Basics: What is useRef?
useRef
creates a box that can hold any value. Unlike useState
, changing what's inside doesn't make your component re-render.
jsx
import { useRef } from 'react';
function SecretBox() {
const myBox = useRef("initial value");
console.log(myBox.current); // "initial value"
return <div>Check the console!</div>;
}
2. Real Use #1: Grabbing DOM Elements
Instead of document.getElementById()
, use useRef
:
jsx
function AutoFocusInput() {
const inputRef = useRef();
// This runs when component loads
useEffect(() => {
inputRef.current.focus(); // Automatically focuses the input!
}, []);
return <input ref={inputRef} />;
}
3. Real Use #2: Tracking Previous State
Need to remember what something was before it changed?
jsx
function Counter() {
const [count, setCount] = useState(0);
const prevCount = useRef();
useEffect(() => {
prevCount.current = count; // Stores without re-render
}, [count]);
return (
<div>
<p>Now: {count}, Before: {prevCount.current}</p>
<button onClick={() => setCount(c => c + 1)}>+</button>
</div>
);
}
4. Real Use #3: Storing Timer IDs
Perfect for setTimeout
/setInterval
:
jsx
function Timer() {
const timerId = useRef();
const startTimer = () => {
timerId.current = setInterval(() => {
console.log("Tick!");
}, 1000);
};
const stopTimer = () => {
clearInterval(timerId.current);
};
return (
<div>
<button onClick={startTimer}>Start</button>
<button onClick={stopTimer}>Stop</button>
</div>
);
}
Common Mistakes to Avoid
Mistake 1: Changing refs during render
jsx
function BadExample() {
const myRef = useRef(0);
myRef.current = 10; // ❌ Don't do this during render!
return <div>{myRef.current}</div>;
}
Fix: Change refs in effects or handlers
jsx
function GoodExample() {
const myRef = useRef(0);
useEffect(() => {
myRef.current = 10; // ✅ Safe!
}, []);
return <div>{myRef.current}</div>;
}
Mistake 2: Using refs when you need state
jsx
function Counter() {
const count = useRef(0); // ❌ Bad - won't trigger updates
return (
<button onClick={() => count.current++}>
{count.current} // This number won't change!
</button>
);
}
Fix: Use useState when UI needs to update
jsx
function Counter() {
const [count, setCount] = useState(0); // ✅ Good
return (
<button onClick={() => setCount(c => c + 1)}>
{count} // Now this works!
</button>
);
}
When Should You Actually Use useRef?
✔ To interact with DOM elements (focus, scroll, measurements)
✔ To store mutable values that shouldn't trigger re-renders
✔ To keep track of previous values
✔ To store timer IDs, animation frames, or other imperatives
Key Takeaways
useRef
is like a persistent storage box for your componentChanging
.current
doesn't cause re-rendersGreat for DOM manipulation and storing "background" values
Don't use it when you need UI updates (use
useState
instead)
Now go try these examples in your code! Which useRef
trick will you use first? 🚀
Subscribe to my newsletter
Read articles from Soniya directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
