When building React applications, there are situations where you may need to directly access or interact with a DOM element or store a mutable value that doesn’t trigger a re-render. This is where useRef() comes in.
What is useRef?
useRef is a hook that returns a mutable object with a .current property. It can be used for two main purposes:
- Accessing DOM elements (like
document.querySelector) - Storing persistent mutable values between renders without re-rendering
Syntax
const myRef = useRef(initialValue);
- The
currentproperty on the object returned byuseRefcan be read or written. - React does not re-render when
.currentchanges.
Example 1: Accessing a DOM Element
Let’s build a simple form where clicking a button will automatically focus the input field.
Code:
import React, { useRef } from 'react';
function FocusInput() {
const inputRef = useRef(null);
const handleClick = () => {
// Access the DOM node directly and focus it
inputRef.current.focus();
};
return (
<div>
<h2>Focus Input Example</h2>
<input ref={inputRef} type="text" placeholder="Click button to focus" />
<button onClick={handleClick}>Focus Input</button>
</div>
);
}
export default FocusInput;
Explanation:
inputRefis initialized withnull.ref={inputRef}binds theinputRefto the input DOM element.inputRef.currentgives direct access to the actual input DOM node.
Example 2: Storing Mutable Values Across Renders
Let’s say you want to count how many times a component has rendered without triggering a re-render.
Code:
import React, { useEffect, useRef, useState } from 'react';
function RenderCounter() {
const [count, setCount] = useState(0);
const renderCount = useRef(1);
useEffect(() => {
renderCount.current += 1;
});
return (
<div>
<h2>Render Counter Example</h2>
<p>Button clicked: {count} times</p>
<p>Component rendered: {renderCount.current} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
export default RenderCounter;
Explanation:
renderCount.currentis updated every time the component re-renders.- Because we’re not using state, the update to
renderCountdoesn’t trigger another render.
Real-World Use Cases
- Focusing inputs or scrolling into view.
- Storing previous props or state (e.g., for comparison).
- Avoiding useEffect race conditions.
- Debounce timers or intervals (e.g., using
setTimeout/setInterval).
useRef vs useState
| Feature | useRef | useState |
|---|---|---|
| Triggers re-render? | No | Yes |
| Persists between renders? | Yes | Yes |
| Common use | DOM access, store mutable values | State management |
Gotchas
- Updating
ref.currentwill not trigger a re-render. - Don’t use
useReffor dynamic values that need to trigger UI updates — useuseStatefor that.
Conclusion
useRef is a powerful and versatile tool in React. Whether you’re managing DOM elements or keeping track of mutable values across renders, useRef can make your code cleaner and more efficient — without unnecessary re-renders.