In React, the useRef hook is useful for interacting with the DOM and maintaining values across renders without causing re-renders.
What is useRef?
useRef is a React Hook that returns a mutable reference object.
This reference persists across renders but does not trigger a re-render when updated. To put it simply, when a component’s state changes, React updates the UI by re-rendering the component. But useRef stores values without triggering this update.
You can think of useRef as a sticky note on your desk. You can update what’s written on it, but it doesn’t affect the layout of your desk. Similarly, useRef stores a value without causing your component to refresh.
Why use useRef?
The useRef hook is useful for:
- Accessing and modifying DOM elements directly.
- Storing values between renders without causing re-renders.
- Tracking previous values in a component.
Basic Syntax of useRef
Before using useRef, you need to import it from React:
import { useRef } from "react";Syntax:
const myRef = useRef(initialValue);Here,
useRef(initialValue): Returns an object{ current: initialValue }- The reference persists across renders but does not cause re-renders when updated.
- You can update the value using
myRef.current.
For example:
import { useRef } from "react";
function Example() {
const countRef = useRef(0); // Creates a ref with initial value 0
const handleClick = () => {
countRef.current += 1; // Updates ref value (no re-render)
console.log("Current count:", countRef.current);
};
return (
<div>
<button onClick={handleClick}>Increment</button>
</div>
);
}Here,
countRefis a mutable reference storing a value.- Clicking the button updates
countRef.current, but the UI does not re-render. - The new value is logged in the console.
Use Case of useRef
Use Case 1: Accessing DOM Elements
If you need to access a DOM element directly without using document.querySelector, you can use the useRef hook.
Example: Setting focus on an input field
import { useRef } from "react";
function FocusInput() {
const inputRef = useRef(null); // Create Ref
const handleClick = () => {
inputRef.current.focus(); // This will focus on the input field
};
return (
<div>
<input ref={inputRef} type="text" placeholder="Enter text" />
<button onClick={handleClick}>Focus Input</button>
</div>
);
}
export default FocusInput;In this example,
useRef(null)creates a reference to store the input element.ref={inputRef}connects the reference to the input field.inputRef.current.focus()sets focus on the input field when the button is clicked.
Use Case 2: Store Value Without Re-render
If you need to store a value without re-rendering a component, you can use the useRef hook.
Example: Tracking the re-render count
import { useState, useRef, useEffect } from "react";
function RenderCounter() {
const [count, setCount] = useState(0);
const renderCount = useRef(0); // Mutable Value (This will not re-render)
useEffect(() => {
renderCount.current += 1; // Increment render count
});
return (
<div>
<p>Count: {count}</p>
<p>Component Re-rendered: {renderCount.current} times</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
export default RenderCounter;In this example,
renderCountpersists across renders without causing extra re-renders.- Using
useStateinstead ofuseRefhere would cause an unnecessary re-render each timerenderCountis updated.
Use Case 3: Track the previous value
You can use the useRef hook to store the previous value of a state.
Example:
import { useState, useEffect, useRef } from "react";
function PreviousValueTracker() {
const [count, setCount] = useState(0);
const prevCountRef = useRef(); // This will store previous value
useEffect(() => {
prevCountRef.current = count; // Update Previous Value
}, [count]);
return (
<div>
<p>Current Count: {count}</p>
<p>Previous Count: {prevCountRef.current}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
export default PreviousValueTracker;Here,
prevCountRefstores the previous value ofcount.useEffectupdatesprevCountRef.currentwhenevercountchanges.
useRef vs useState: When to use?
| Feature | useRef | useState |
|---|---|---|
| Value Persistence | Yes, persists across renders | Yes, persists across renders |
| Re-render Trigger | No re-render on update | Triggers re-render |
| Use Case | DOM access, previous values | UI updates, state management |
| Initial Value | { current: initialValue } | Any data type supported |
- If you need to update the UI, use the
useStatehook. - If you need to store a value without triggering a re-render, use the
useRefhook.
Best Practices & Common Mistakes of useRef
Mistake: Using useRef for UI updates
const countRef = useRef(0);
const increment = () => {
countRef.current += 1;
console.log(countRef.current); // This will update the value, but not the UI
};Best Practice: For UI updates, use the useState hook.
Mistake: Using useRef for executing side effects
function App() {
const ref = useRef(0);
if (ref.current === 0) {
console.log("Component mounted!"); // This will execute multiple times
}
ref.current++;
}Here, useRef values persist between renders, but component re-renders don’t reset their value. So the condition (if (ref.current === 0)) will always be true on re-render, causing repeated execution.
Best Practice: Use the useEffect hook for side effects.
Example:
useEffect(() => {
console.log("Component mounted!");
}, []);Why You Need useRef?
- Efficiently access DOM elements.
- Store values across renders without unnecessary re-renders.
- Track previous state values.