React useRef Hook: Manage DOM and Persistent Values Without Re-Renders
Last updated 4 months, 2 weeks ago | 192 views 75 5
Introduction: Why useRef Matters in React
React’s rendering system is powerful, but sometimes, you need to store values that don’t trigger re-renders—like tracking a timer ID or referencing a DOM element. That’s where the useRef Hook shines.
The useRef Hook is often overlooked but extremely handy for scenarios where:
-
You need to directly interact with a DOM element.
-
You want to persist values between renders without causing a re-render.
-
You need to avoid using
useStatefor non-UI-related values.
Think of useRef as a box that holds a mutable value. You can change the value inside without causing your component to re-render.
Understanding useRef (Step-by-Step)
1. Basic Syntax
const myRef = useRef(initialValue);
-
The
.currentproperty holds the mutable value. -
Unlike state, updating
.currentdoesn’t trigger a re-render.
2. Accessing DOM Elements
You can use useRef to reference a DOM node, similar to document.getElementById.
import React, { useRef, useEffect } from 'react';
function FocusInput() {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus(); // Focus the input on mount
}, []);
return <input ref={inputRef} placeholder="Type here..." />;
}
3. Storing Mutable Values
useRef can store any value that persists across renders but doesn’t affect the UI.
function TimerTracker() {
const count = useRef(0);
const handleClick = () => {
count.current++;
console.log(`Clicked ${count.current} times`);
};
return <button onClick={handleClick}>Click Me</button>;
}
-
Note: This will not re-render the component when
count.currentchanges.
4. Avoiding Unnecessary Re-Renders
If you store frequent updates in useState, it causes re-renders. useRef avoids that.
Comparison Table: useRef vs useState
| Feature | useRef |
useState |
|---|---|---|
| Triggers re-render | ❌ No | ✅ Yes |
| Persists across renders | ✅ Yes | ✅ Yes |
| Stores DOM refs | ✅ Yes | ❌ No |
| Best use case | Timer ID, DOM access, caches | UI state, form values, etc. |
Complete Working Example: Form Focus & Timer Tracker
import React, { useRef, useEffect } from 'react';
function App() {
const inputRef = useRef(null);
const timerRef = useRef(null);
useEffect(() => {
inputRef.current.focus(); // Autofocus input on mount
timerRef.current = setInterval(() => {
console.log('Timer running...');
}, 1000);
return () => {
clearInterval(timerRef.current); // Cleanup on unmount
};
}, []);
const handleClick = () => {
alert(`Current input value: ${inputRef.current.value}`);
};
return (
<div style={{ padding: '20px' }}>
<input ref={inputRef} placeholder="Enter something..." />
<button onClick={handleClick} style={{ marginLeft: '10px' }}>
Show Input
</button>
</div>
);
}
export default App;
Tips & Common Pitfalls
✅ Best Practices
-
Use
useReffor DOM manipulation or storing non-UI values. -
Name your refs clearly (
inputRef,timerRef) for readability. -
Clean up timers or subscriptions inside
useEffect.
⚠️ Common Mistakes
-
Expecting re-renders: Changing
.currentwon’t re-render the component. -
Accessing
.currentbefore mount: Always check if it'snullwhen dealing with DOM refs. -
Overusing refs: Don’t use them for everything—state is still the right tool for UI-driven logic.
When to Use useRef vs Other Hooks
| Scenario | Recommended Hook |
|---|---|
| Accessing DOM elements | useRef |
| Storing interval/timer IDs | useRef |
| Persisting non-UI data | useRef |
| UI-driven data (triggers re-render) | useState |
| Sharing logic between components | useContext |
Conclusion: Master useRef for Clean, Efficient React Code
The useRef Hook is your best friend for dealing with:
-
Direct DOM interactions
-
Persistent mutable values
-
Avoiding performance hits from re-renders
Keep in mind: If your value doesn't affect the UI directly and doesn't need to trigger a render—useRef is likely what you need.
✅ Actionable Takeaways
-
Use
useRefto focus inputs or access other DOM nodes. -
Store mutable values like counters, timers, or cache references.
-
Avoid overusing state for non-visual logic—
useRefis more performant. -
Combine
useRefwithuseEffectfor timed or delayed logic.