useRef має два різні призначення, і з TypeScript це видно в типах.
1. Посилання на DOM-елемент:
const inputRef = useRef<HTMLInputElement>(null);
// тип: RefObject<HTMLInputElement | null>
useEffect(() => {
inputRef.current?.focus(); // до монтування - null, тому ?.
}, []);
return <input ref={inputRef} />;
Тип елемента - відповідний клас DOM: HTMLInputElement, HTMLDivElement, HTMLCanvasElement. Якщо вказати не той (HTMLDivElement для <input>), TypeScript повідомить про помилку в атрибуті ref.
2. Змінне значення, що не викликає рендер:
const timerId = useRef<number | null>(null);
const renders = useRef(0); // RefObject<number>, тип виведено
function start() {
timerId.current = window.setInterval(tick, 1000);
}
Що змінилося в типах React 19:
useRefвимагає аргумент.useRef<number>()без початкового значення - помилка компіляції; треба явноuseRef<number | undefined>(undefined);currentзавжди можна змінювати. РанішеuseRef<T>(null)повертавRefObjectзcurrentлише для читання, аuseRef<T>(initial)-MutableRefObject, і плутанина між ними давала незрозумілі помилки. Тепер є одинRefObject<T>зі зміннимcurrent;MutableRefObjectоголошено застарілим.
window.setInterval, а не setInterval: у проєктах, де є і типи DOM, і типи Node.js, глобальний setInterval повертає NodeJS.Timeout, а не число. Явний window. прибирає конфлікт, або тип ref - ReturnType<typeof setInterval>.
Колбек-ref з очищенням (React 19):
<div ref={(node) => {
if (!node) return;
const observer = new ResizeObserver(onResize);
observer.observe(node);
return () => observer.disconnect(); // функція очищення
}} />
У TypeScript колбек-ref тепер не може неявно повертати значення: ref={(node) => (instance = node)} - помилка, бо присвоєння повертає значення, яке React вважав би функцією очищення. Потрібні фігурні дужки.
Правило з документації: не читати й не змінювати ref.current під час рендеру (крім лінивої ініціалізації) - лише в обробниках подій та ефектах.