Як типізувати useRef()?
1. Що робить useRef
useRef створює об'єкт-посилання ({ current: ... }),
який React зберігає між рендерами.
Тип сигнатури:
function useRef<T>(initialValue: T): MutableRefObject<T>;2. Базова типізація
const countRef = useRef<number>(0);
countRef.current++; // current - числоTypeScript знає, що countRef.current - number.
Якщо передаєш початкове значення, TS виведе тип автоматично:
const flagRef = useRef(false); // inferred: MutableRefObject<boolean>3. Якщо спочатку null (DOM або lazy-посилання)
Коли ref спочатку null - обов'язково вказуємо тип явно:
// Посилання на DOM-елемент
const inputRef = useRef<HTMLInputElement | null>(null);Тут current може бути null до монтування.
При використанні:
inputRef.current?.focus();Безпечно: ?. захищає від null.
4. Передавання ref в елемент
export function Form() {
const inputRef = useRef<HTMLInputElement | null>(null);
return <input ref={inputRef} type="text" />;
}TypeScript сам знає, що inputRef.current - HTMLInputElement | null.
Для різних елементів:
HTMLDivElementHTMLButtonElementHTMLFormElementHTMLTextAreaElementHTMLAnchorElementі т.д.
5. Якщо потрібно тільки зберігати значення (а не DOM)
const intervalId = useRef<number | null>(null);
useEffect(() => {
intervalId.current = window.setInterval(() => console.log('tick'), 1000);
return () => clearInterval(intervalId.current!);
}, []);Корисно для зберігання mutable-змінних без ререндера.
6. Приклад з forwardRef
type InputProps = React.ComponentProps<"input">;
const Input = React.forwardRef<HTMLInputElement, InputProps>((props, ref) => {
return <input ref={ref} {...props} />;
});
// Використання
const inputRef = useRef<HTMLInputElement>(null);
<Input ref={inputRef} />;forwardRef приймає два параметри типів:
React.forwardRef<Тип_елемента, Тип_пропсів>()7. Коли потрібен useRef<T | null>(null) vs useRef<T>(...)
| Сценарій | Тип |
|---|---|
| DOM-елемент (монтується пізніше) | useRef<T | null>(null) |
| Проста змінна (число, об'єкт тощо) | useRef<T>(значення) |
| Lazy-ініціалізація | useRef<T | null>(null) |
8. "Readonly ref" (незмінне посилання)
Якщо посилання не змінюватиметься вручну,
можна використати React.RefObject<T>:
const divRef: React.RefObject<HTMLDivElement> = useRef(null);RefObject = { readonly current: T | null }
Використовується, коли ти просто передаєш ref без модифікацій.
9. Приклад: клік поза елементом
function Dropdown() {
const menuRef = useRef<HTMLDivElement | null>(null);
useEffect(() => {
function handleClick(e: MouseEvent) {
if (!menuRef.current?.contains(e.target as Node)) {
console.log("Clicked outside");
}
}
document.addEventListener("click", handleClick);
return () => document.removeEventListener("click", handleClick);
}, []);
return <div ref={menuRef}>Menu</div>;
}menuRef.current типізований коректно.
contains() викликається безпечно.
Підсумок
| Сценарій | Типізація |
|---|---|
| Проста ref-змінна | useRef<number>(0) |
| DOM ref | useRef<HTMLDivElement | null>(null) |
ref на <input> | useRef<HTMLInputElement | null>(null) |
| ref для таймера / id | useRef<number | null>(null) |
| Тільки читання | React.RefObject<T> |
З forwardRef | React.forwardRef<T, Props>() |
Коротко:
- Завжди додавай
| null, якщоrefна DOM.- Не пиши
any. Використовуй точний тип елемента (HTMLDivElement,HTMLInputElementтощо).- Для зберігання "mutable" даних без ререндера -
useRef<T>().- Для посилань між компонентами -
forwardRef()з generic.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.