Skip to main content

Як типізувати useRef()?

1. Що робить useRef

useRef створює об'єкт-посилання ({ current: ... }), який React зберігає між рендерами.

Тип сигнатури:

javascript
function useRef<T>(initialValue: T): MutableRefObject<T>;

2. Базова типізація

javascript
const countRef = useRef<number>(0); countRef.current++; // current - число

TypeScript знає, що countRef.current - number. Якщо передаєш початкове значення, TS виведе тип автоматично:

javascript
const flagRef = useRef(false); // inferred: MutableRefObject<boolean>

3. Якщо спочатку null (DOM або lazy-посилання)

Коли ref спочатку null - обов'язково вказуємо тип явно:

javascript
// Посилання на DOM-елемент const inputRef = useRef<HTMLInputElement | null>(null);

Тут current може бути null до монтування. При використанні:

javascript
inputRef.current?.focus();

Безпечно: ?. захищає від null.


4. Передавання ref в елемент

javascript
export function Form() { const inputRef = useRef<HTMLInputElement | null>(null); return <input ref={inputRef} type="text" />; }

TypeScript сам знає, що inputRef.current - HTMLInputElement | null.

Для різних елементів:

  • HTMLDivElement
  • HTMLButtonElement
  • HTMLFormElement
  • HTMLTextAreaElement
  • HTMLAnchorElement і т.д.

5. Якщо потрібно тільки зберігати значення (а не DOM)

javascript
const intervalId = useRef<number | null>(null); useEffect(() => { intervalId.current = window.setInterval(() => console.log('tick'), 1000); return () => clearInterval(intervalId.current!); }, []);

Корисно для зберігання mutable-змінних без ререндера.


6. Приклад з forwardRef

javascript
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 приймає два параметри типів:

javascript
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>:

javascript
const divRef: React.RefObject<HTMLDivElement> = useRef(null);

RefObject = { readonly current: T | null }

Використовується, коли ти просто передаєш ref без модифікацій.


9. Приклад: клік поза елементом

javascript
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 refuseRef<HTMLDivElement | null>(null)
ref на <input>useRef<HTMLInputElement | null>(null)
ref для таймера / iduseRef<number | null>(null)
Тільки читанняReact.RefObject<T>
З forwardRefReact.forwardRef<T, Props>()

Коротко:

  • Завжди додавай | null, якщо ref на DOM.
  • Не пиши any. Використовуй точний тип елемента (HTMLDivElement, HTMLInputElement тощо).
  • Для зберігання "mutable" даних без ререндера - useRef<T>().
  • Для посилань між компонентами - forwardRef() з generic.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.