Mastering React useRef: DOM Access and Persistent Storage
Unlike useState, which triggers a re-render every time its value changes, useRef provides a "hidden" container that persists across re-renders without notifying React to update the UI.
1. The Core Mechanics
useRef returns a mutable object with a single property: { current: initialValue }.
Mutation: You can change
ref.currentat any time.Rendering: Changing
ref.currentdoes not trigger a re-render of your component.
2. Use Case 1: Direct DOM Access
While React usually handles the UI declaratively, there are times you need to interact with the DOM directly, such as focusing an input, playing a video, or measuring an element’s scroll position.
import { useRef } from 'react';
export default function SearchForm(){
const inputRef = useRef(null);
const handleFocus = () => {
// Access the DOM element directly via .current
inputRef.current.focus();
};
return (
<div>
<input ref={inputRef} placeholder="Search..." />
<button onClick={handleFocus}>Focus Input</button>
</div>
);
}3. Use Case 2: Persisting Values Without Re-renders
If you need to keep track of a value (like an Interval ID or a previous state) that shouldn’t affect the visual output, useRef is the correct choice.
Stopwatch Example (Storing Timer ID)
const [time, setTime] = useState(0);
const timerRef = useRef(null); // Stores ID without triggering re-renders
const startTimer = () => {
if (timerRef.current) return;
timerRef.current = setInterval(() => {
setTime(prev => prev + 1);
}, 1000);
};
const stopTimer = () => {
clearInterval(timerRef.current);
timerRef.current = null;
};Essential Best Practices
Avoid rendering refs: Do not try to display
ref.currentdirectly in your JSX. Because updates to refs don't trigger re-renders, the UI will stay stale. UseuseStatefor anything that needs to be visible in the UI.Wait for the mount: Never access
ref.currentduring the component's initial render (the component body). The DOM element will benullat that stage. Always access refs insideuseEffector event handlers.Stay pure: Treat
ref.currentas a private property of the component.
Conclusion
useRef is your tool for "escaping" the React render cycle when necessary. Whether you are managing focus, scrolling, or tracking background IDs, understanding the distinction between when to trigger a render (useState) and when to simply store a value (useRef) is a key skill for optimizing React application performance.