useRef Hook
What Is a Ref?
useRef returns a mutable object with a .current property. This object persists across the entire component lifecycle without causing a re-render when changed.
useRef for DOM Access
import React, { useRef, useEffect } from 'react';
function TextInput() {
const inputRef = useRef(null);
const focusInput = () => {
inputRef.current.focus();
};
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={focusInput}>Focus Input</button>
</div>
);
}
useRef as a Mutable Container
useRef can store any value that persists between renders without triggering a re-render.
function Stopwatch() {
const [time, setTime] = React.useState(0);
const intervalRef = useRef(null);
const start = () => {
intervalRef.current = setInterval(() => {
setTime(prev => prev + 1);
}, 1000);
};
const stop = () => {
clearInterval(intervalRef.current);
};
React.useEffect(() => {
return () => clearInterval(intervalRef.current);
}, []);
return (
<div>
<p>{time}</p>
<button onClick={start}>Start</button>
<button onClick={stop}>Stop</button>
</div>
);
}
Differences Between useRef and useState
| Aspect | useState |
useRef |
|---|---|---|
| Re-renders on update | Yes | No |
| DOM access | Not direct | Direct |
| Value type | Primitive/state | Mutable object |
| Persists across renders | Yes | Yes |
function Comparison() {
const [count, setCount] = React.useState(0);
const countRef = useRef(0);
const handleState = () => setCount(count + 1); // Re-renders
const handleRef = () => { countRef.current += 1; }; // No re-render
return (
<div>
<p>State: {count}</p>
<p>Ref: {countRef.current}</p>
<button onClick={handleState}>State + 1</button>
<button onClick={handleRef}>Ref + 1</button>
</div>
);
}
Example: Input Focus
function SearchForm() {
const searchRef = useRef(null);
useEffect(() => {
searchRef.current.focus();
}, []);
const handleSubmit = (e) => {
e.preventDefault();
alert(`Searching: ${searchRef.current.value}`);
};
return (
<form onSubmit={handleSubmit}>
<input ref={searchRef} type="search" placeholder="Search..." />
<button type="submit">Search</button>
</form>
);
}
Scrolling to an Element
function CommentSection() {
const bottomRef = useRef(null);
const [comments, setComments] = React.useState([]);
const addComment = (text) => {
setComments(prev => [...prev, text]);
// Scroll to the newest comment
setTimeout(() => {
bottomRef.current.scrollIntoView({ behavior: 'smooth' });
}, 100);
};
return (
<div>
{comments.map((c, i) => (
<p key={i}>{c}</p>
))}
<div ref={bottomRef} />
<button onClick={() => addComment('New comment')}>Add</button>
</div>
);
}
useRef is useful for DOM access and for values that do not need to trigger a render. Next we will study useCallback and useMemo for performance optimization.