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,
countRef
is 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,
renderCount
persists across renders without causing extra re-renders.- Using
useState
instead ofuseRef
here would cause an unnecessary re-render each timerenderCount
is 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,
prevCountRef
stores the previous value ofcount
.useEffect
updatesprevCountRef.current
whenevercount
changes.
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
useState
hook. - If you need to store a value without triggering a re-render, use the
useRef
hook.
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.