Introduction
When you think of React, you usually think about state, props, and re-rendering. But sometimes you just need a way to:
- Grab a DOM element directly
- Store a value without causing a re-render
- Keep track of something between renders
That’s exactly where the useRef hook comes in.
In this post, I’ll explain useRef like I would to a teammate, and we’ll go over practical examples you’ll actually use in the real world.
What Is useRef?
useRef is a React Hook that gives you a mutable object that doesn’t trigger re-renders when updated.
const myRef = useRef(initialValue);
- It returns an object like
{ current: value } - You can update
myRef.current = newValue - The value persists across renders
- It does not cause a re-render when it changes
useRef for Accessing DOM Elements
The most common use case: grabbing DOM nodes.
Let’s say you want to auto-focus an input field when a component loads.
import React, { useRef, useEffect } from 'react';
function SearchInput() {
const inputRef = useRef();
useEffect(() => {
inputRef.current.focus(); // Focus the input on mount
}, []);
return <input ref={inputRef} placeholder="Type to search..." />;
}What’s Happening?
inputRef.currentgives us the DOM element.- We can now call native methods like
.focus(),.scrollIntoView(), etc.
This is super helpful in:
- Login forms
- Modals
- Chat apps
- Search UIs
useRef to Store Mutable State (That Doesn’t Re-render)
Sometimes you want to keep a value without triggering re-renders.
Here’s an example of a timer using setInterval:
import React, { useRef, useState, useEffect } from 'react';
function Timer() {
const [count, setCount] = useState(0);
const intervalRef = useRef();
useEffect(() => {
intervalRef.current = setInterval(() => {
setCount((prev) => prev + 1);
}, 1000);
return () => clearInterval(intervalRef.current);
}, []);
return <h1>Time: {count}s</h1>;
}Why Not useState for interval?
Because useState would re-render every time we store the timer ID.useRef keeps it safely stored without causing unnecessary re-renders.
useRef to Track Previous Values
Another practical trick — track the previous value of a prop or state.
import React, { useEffect, useRef, useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const prevCount = useRef();
useEffect(() => {
prevCount.current = count;
}, [count]);
return (
<div>
<p>Current: {count}</p>
<p>Previous: {prevCount.current}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}This is super helpful in:
- Animations
- Comparing prop/state changes
- Building undo/redo features
useRef vs useState — When to Use What?
| Scenario | useRef | useState |
|---|---|---|
| Needs to persist across renders | Yes | Yes |
| Triggers a re-render when changed | No | Yes |
| Stores a DOM node reference | Yes | No |
| Tracks a value but shouldn’t re-render UI | Yes | No |
Rule of Thumb:
Use useRef when you need to remember something but don’t want it to affect rendering.
Bonus: useRef in Form Validation
Let’s say you’re checking input field validity on form submit:
function LoginForm() {
const usernameRef = useRef();
const passwordRef = useRef();
const handleSubmit = (e) => {
e.preventDefault();
const username = usernameRef.current.value;
const password = passwordRef.current.value;
if (!username || !password) {
alert("Both fields are required!");
} else {
alert(`Welcome, ${username}!`);
}
};
return (
<form onSubmit={handleSubmit}>
<input ref={usernameRef} placeholder="Username" />
<input ref={passwordRef} type="password" placeholder="Password" />
<button type="submit">Login</button>
</form>
);
}No need for extra useState if you’re not updating the UI based on input changes.
What useRef Is Not For
- It’s not a state replacement for anything that changes the UI
- It won’t notify React when updated
- You shouldn’t use it for things like “show/hide UI”
Summary
useRef lets you:
- Access DOM nodes (like
.focus()) - Store timers or values across renders
- Keep previous values
- Build more optimized components
It’s one of those quiet heroes in React—once you really get it, you’ll use it everywhere (for the right reasons).
