🧭 Introduction
Most developers learn useRef like this:
“useRef is used to access DOM elements.”
While that is true, it is also only 20% of the story.
In real-world React applications, useRef is a powerful advanced hook used for:
- Persisting values without re-rendering
- Avoiding unnecessary renders
- Managing timers, intervals, and previous values
- Solving problems that
useStatecannot
In this lesson, you’ll learn what useRef really is, how it works, and when to use it correctly.
🎯 What You’ll Learn in This Lesson
By the end of this lesson, you will understand:
- What
useRefactually returns - Difference between
useRefanduseState - DOM access use cases
- Persisting mutable values
- Tracking previous values
- Common mistakes and best practices
🧠 What is useRef? (Clear Definition)
useRef is:
A hook that returns a mutable object whose value persists across renders without causing re-renders.
Syntax:
const ref = useRef(initialValue);
It returns:
{ current: initialValue }
🔑 Key Properties of useRef
✅ Value persists between renders
✅ Updating .current does not trigger re-render
✅ Same ref object is returned every render
This makes it very different from useState.
🆚 useRef vs useState (Critical Comparison)
| Feature | useRef | useState |
|---|---|---|
| Causes re-render | ❌ No | ✅ Yes |
| Persists value | ✅ Yes | ✅ Yes |
| Mutable | ✅ Yes | ❌ No |
| Triggers UI update | ❌ No | ✅ Yes |
| Best for | Non-UI data | UI data |
👉 Golden Rule:
If the value affects UI → useState
If it does not → useRef
🧩 Basic DOM Access Example
This is the most common use case.
import { useRef } from "react";
function InputFocus() {
const inputRef = useRef(null);
const focusInput = () => {
inputRef.current.focus();
};
return (
<>
<input ref={inputRef} />
<button onClick={focusInput}>Focus</button>
</>
);
}
👉 React does not re-render when .focus() is called.
⚠ Important Note About Timing
ref.current is:
nullon first render- Assigned after DOM is mounted
That’s why DOM refs are usually used:
- Inside event handlers
- Inside effects
🔁 Persisting Values Across Renders (Real Power)
❌ Wrong Approach with useState
const [timerId, setTimerId] = useState(null);
This causes unnecessary re-renders.
✅ Correct Approach with useRef
const timerId = useRef(null);
Perfect for:
- Timers
- Intervals
- WebSocket connections
- Previous values
🧪 Example — Interval with useRef
function Timer() {
const intervalRef = useRef(null);
const start = () => {
intervalRef.current = setInterval(() => {
console.log("Running...");
}, 1000);
};
const stop = () => {
clearInterval(intervalRef.current);
};
return (
<>
<button onClick={start}>Start</button>
<button onClick={stop}>Stop</button>
</>
);
}
👉 No re-render needed for interval ID.
🔍 Tracking Previous Value (Advanced Pattern)
React does not give previous state automatically.useRef solves this cleanly.
function Counter({ count }) {
const prevCount = useRef(count);
useEffect(() => {
prevCount.current = count;
}, [count]);
return (
<p>
Current: {count}, Previous: {prevCount.current}
</p>
);
}
👉 useRef stores previous value without affecting render.
🧠 Why useRef Does NOT Trigger Re-render
React:
- Tracks state changes for rendering
- Ignores ref mutations
Because refs are meant for:
“Escape hatches” from React’s rendering cycle
🚨 Common Mistakes with useRef
❌ Mistake 1: Expecting UI to Update
ref.current++;
❌ UI won’t update
✅ Use state for UI data
❌ Mistake 2: Using useRef Instead of State Everywhere
useRef is not a replacement for state.
❌ Mistake 3: Mutating State Instead of Using Ref
state.value = 10; // ❌
🧠 useRef in Real-World Apps
Professional use cases:
- Form libraries (React Hook Form)
- Animation libraries
- Measuring DOM size
- Avoiding stale closures
- Performance optimization
🔗 useRef & Closures (Important Insight)
Closures capture values at render time.
Refs always point to latest value.
This makes useRef useful for:
- Event handlers
- Async callbacks
- Intervals
🎯 Best Practices
✅ Use refs for non-visual data
✅ Prefer state for UI updates
✅ Keep ref usage intentional
✅ Don’t abuse refs
❓ FAQs — useRef Hook
🔹 Can useRef replace useState?
No. They solve different problems.
🔹 Does updating ref cause re-render?
No — and that’s the point.
🔹 Is useRef safe?
Yes, when used correctly.
🔹 Why is useRef called an escape hatch?
Because it bypasses React’s render cycle.
🧠 Quick Recap
✔ useRef stores mutable values
✔ Does not cause re-render
✔ Perfect for DOM access & timers
✔ Useful for previous values
✔ Essential advanced hook
🎉 Conclusion
useRef is one of the most misunderstood hooks — yet one of the most powerful.
Once you master it:
- You’ll write cleaner logic
- You’ll avoid unnecessary renders
- You’ll solve problems that state cannot
This lesson completes your understanding of non-rendering state in React ⚛️🧠
👉 Next Lesson
Lesson 6 — forwardRef in React (Real-World Use Cases)