useRef
useRef returns a stable mutable object. The classic use is a DOM ref; it’s also the right tool for any value you want to read/write WITHOUT triggering a re-render — timers, previous values, instance counters.
DOM refs, timers, previous-value pattern
EXAMPLE
import { useRef, useEffect, useState } from 'react';
// 1) DOM reference
function Search() {
const inputRef = useRef(null);
useEffect(() => { inputRef.current?.focus(); }, []);
return <input ref={inputRef} />;
}
// 2) Mutable value (no re-render)
function DoubleTap({ onDouble }) {
const lastTap = useRef(0);
function onPress() {
const now = Date.now();
if (now - lastTap.current < 300) onDouble();
lastTap.current = now;
}
return <button onClick={onPress}>tap</button>;
}
// 3) Hold timer / interval IDs
function Timer() {
const id = useRef(null);
const [n, setN] = useState(0);
useEffect(() => {
id.current = setInterval(() => setN(c => c + 1), 1000);
return () => clearInterval(id.current);
}, []);
return <p>{n}s</p>;
}
// 4) Previous-value pattern
function usePrevious(value) {
const ref = useRef();
useEffect(() => { ref.current = value; }, [value]);
return ref.current;
}
function Diff({ value }) {
const prev = usePrevious(value);
return <p>now {value}, was {prev ?? '—'}</p>;
}
// 5) Imperative handle — expose methods up
import { forwardRef, useImperativeHandle } from 'react';
const Modal = forwardRef(function Modal(props, ref) {
const dialogRef = useRef(null);
useImperativeHandle(ref, () => ({
open: () => dialogRef.current.showModal(),
close: () => dialogRef.current.close(),
}));
return <dialog ref={dialogRef}>…</dialog>;
});
Why it matters
useRef is for values that change but shouldn’t trigger renders. Anything you’d normally store as this.foo in a class component goes in a ref now.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
const inputRef = useRef(null);
useEffect(() => inputRef.current?.focus(), []);
return <input ref={inputRef} />;
Try it Yourself »
Discussion
Loading…