useRef Hook
useRef returns a mutable object { current: initialValue } that persists for the lifetime of the component. Changing ref.current does not trigger a re-render. useRef has two main uses:
Use 1 โ Accessing DOM Elements
function AutoFocusInput() {
const inputRef = useRef(null);
useEffect(() => {
// After first render, focus the input
inputRef.current.focus();
}, []);
return <input ref={inputRef} placeholder="Auto-focused on mount" />;
}
// Scrolling to an element
function ScrollToBottom() {
const bottomRef = useRef(null);
const scrollDown = () =>
bottomRef.current.scrollIntoView({ behavior: "smooth" });
return (
<div>
<button onClick={scrollDown}>Scroll to bottom</button>
{/* ... long content ... */}
<div ref={bottomRef} />
</div>
);
}
Use 2 โ Storing Mutable Values Without Re-renders
function Stopwatch() {
const [elapsed, setElapsed] = useState(0);
const intervalRef = useRef(null); // Store interval ID โ changing it won't re-render
const start = () => {
intervalRef.current = setInterval(() => {
setElapsed(e => e + 1);
}, 1000);
};
const stop = () => clearInterval(intervalRef.current);
const reset = () => { stop(); setElapsed(0); };
// Cleanup on unmount
useEffect(() => () => clearInterval(intervalRef.current), []);
return (
<div>
<p>{elapsed}s</p>
<button onClick={start}>Start</button>
<button onClick={stop}>Stop</button>
<button onClick={reset}>Reset</button>
</div>
);
}
Tracking Previous Value
// Custom hook to track previous value of any variable
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value; // Update AFTER render
});
return ref.current; // Returns value from PREVIOUS render
}
function PriceDisplay({ price }) {
const prevPrice = usePrevious(price);
const changed = prevPrice !== undefined && price !== prevPrice;
return (
<p style={{ color: changed ? (price > prevPrice ? "green" : "red") : "inherit" }}>
${price.toFixed(2)}
{changed && (price > prevPrice ? " โฒ" : " โผ")}
</p>
);
}