When working with React, you may have come across the useRef hook. While often associated with accessing DOM elements, useRef is far more versatile than it first appears. In this blog post, we’ll dive deep into what useRef is, when and why to use it, and conclude with a practical example to help clarify its real-world utility.
What is useRef?
useRef is a React hook that gives you a changeable object, which you can use to hold values without causing re-renders. This object stays the same throughout the whole life of the component. Unlike state variables, changes to a ref do It doesn’t cause the component to re-render, which makes it ideal for keeping data that should last across renders without changing the user interface.
Here’s the syntax:
const myRef = useRef(initialValue);
The object you get from useRef() contains one main property called current.
Common Use Cases of useRef
1. Accessing DOM Elements : This is the most straightforward and well-known use of useRef. If you need to interact directly with the DOM (like focusing an input or measuring a component’s size), useRef is your go-to.
Example:
const inputRef = useRef();
const focusInput = () => {
inputRef.current.focus();
};
return <input ref={inputRef} />;
2. Persisting Values Across Renders : State updates trigger a re-render, but refs do not. If you want to keep track of values between renders without causing re-renders, refs are perfect.
const countRef = useRef(0);
useEffect(() => {
countRef.current += 1;
console.log(`Component rendered ${countRef.current} times`);
});
3. Storing Previous State or Props : Tracking previous values is a common requirement, especially when comparing previous and current values inside useEffect.
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
4. Avoiding Closure Traps in Asynchronous Logic : Refs can also be used to access the latest value in asynchronous callbacks without adding them to the dependency array of useEffect or re-declaring the function.
Example:
const latestValue = useRef(value);
useEffect(() => {
latestValue.current = value;
}, [value]);
function handleTimeout() {
setTimeout(() => {
console.log(latestValue.current); // Always gets the latest value
}, 1000);
}
A Practical Example :
Click Counter Without Re-renders Let’s build a simple component that counts how many times a button is clicked — without causing re-renders every time the count updates.
import React, { useRef } from 'react';
function SilentCounter() {
const clickCountRef = useRef(0);
const handleClick = () => {
clickCountRef.current += 1;
console.log(`Button clicked ${clickCountRef.current} times`);
};
return (
<div>
<h2>Click the button and check the console!</h2>
<button onClick={handleClick}>Click Me</button>
</div>
);
}
export default SilentCounter;
Why this works :
- The clickCountRef stores the number of clicks.
- It doesn’t cause a re-render on update.
- The console logs every click without affecting the UI’s performance.
- This is especially useful in high-frequency interactions or in components where unnecessary re-renders can cause lag or flickering.
Final Thoughts : The useRef hook is more than just a DOM reference tool. It’s a powerful utility for handling mutable values that shouldn’t trigger re-renders. Whether you’re building performance-critical features or managing timers and previous values, useRef gives you a flexible and effective way to retain data across renders.
Next time you're tempted to reach for state, consider if a ref might serve you better. 😊
— Sudhir Yadav, Senior Software Engineer