Understanding useRef in React (Beginner Friendly Guide)
React provides several hooks to manage state and behavior in functional components. One of the most commonly misunderstood hooks is useRef. While it looks simple, it solves important problems related to rendering and DOM interaction.
This blog explains useRef with clear concepts, practical examples, and when to use it correctly.
What is useRef?
useRef is a React Hook that returns a mutable object with a .current property.
const ref = useRef(initialValue);
This object persists for the entire lifetime of the component.
Key Characteristics of useRef
- Stores a value that does not trigger re-render
- Keeps the same reference across renders
- Can directly access DOM elements
- Works like a container that holds mutable data
How useRef Works Internally
When you write:
const myRef = useRef(10);
React internally creates an object like:
{
current: 10
}
Important points:
- This object is not recreated on every render
- Only the
.currentvalue changes - React does not track changes inside
.current
Why useRef is Needed
React components re-render when state or props change. However, not all data changes should trigger a re-render.
There are two main problems useRef solves:
1. Avoid unnecessary re-renders
Using useState for every value can cause performance issues.
2. Direct DOM manipulation
React is declarative, but some cases require imperative control (like focusing an input).
Accessing DOM Elements
import { useEffect, useRef } from "react";
function App() {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus();
}, []);
return <input ref={inputRef} />;
}
Explanation:
-
refattribute connects the DOM element toinputRef -
inputRef.currentgives direct access to the DOM node - The focus method is called after render
Persisting Values Without Re-render
import { useRef } from "react";
function App() {
const countRef = useRef(0);
const handleClick = () => {
countRef.current += 1;
console.log(countRef.current);
};
return <button onClick={handleClick}>Click</button>;
}
Here:
- Value updates
- Component does not re-render
- Useful for storing non-UI data
Tracking Previous State
import { useEffect, useRef, useState } from "react";
function App() {
const [count, setCount] = useState(0);
const prevCount = useRef();
useEffect(() => {
prevCount.current = count;
}, [count]);
return (
<>
<p>Current: {count}</p>
<p>Previous: {prevCount.current}</p>
<button onClick={() => setCount(count + 1)}>Increase</button>
</>
);
}
This pattern is useful when comparing previous and current values.
Working with Timers
import { useRef } from "react";
function App() {
const timerRef = useRef(null);
const start = () => {
timerRef.current = setInterval(() => {
console.log("Running...");
}, 1000);
};
const stop = () => {
clearInterval(timerRef.current);
};
return (
<>
<button onClick={start}>Start</button>
<button onClick={stop}>Stop</button>
</>
);
}
useRef helps store the interval ID across renders.
useRef vs useState (Conceptual Difference)
| Concept | useState | useRef |
|---|---|---|
| Triggers re-render | Yes | No |
| Data visibility | Used in UI | Not directly used in UI |
| Mutability | Immutable updates required | Mutable (.current can change) |
| Lifecycle behavior | Re-initialized logically | Persisted across renders |
When to Use useRef
Use useRef when:
- You need direct access to a DOM element
- You want to store mutable data without re-render
- You need to persist values between renders
- You are working with timers, intervals, or external libraries
When Not to Use useRef
Avoid using useRef when:
- The value should be visible in UI
- UI must update when value changes
- State management is required
In these cases, useState is the correct choice.
Common Mistakes
1. Forgetting .current
inputRef.focus(); // incorrect
Correct:
inputRef.current.focus();
2. Using useRef for UI updates
Changing .current will not update the UI.
3. Expecting reactivity
useRef is not reactive like state.
Mental Model
-
useState→ reactive data (affects UI) -
useRef→ persistent container (does not affect UI)
Top comments (0)