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
current
property on the object returned byuseRef
can be read or written. - React does not re-render when
.current
changes.
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:
inputRef
is initialized withnull
.ref={inputRef}
binds theinputRef
to the input DOM element.inputRef.current
gives 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.current
is updated every time the component re-renders.- Because we’re not using state, the update to
renderCount
doesn’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.current
will not trigger a re-render. - Don’t use
useRef
for dynamic values that need to trigger UI updates — useuseState
for 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.