Коротка відповідь
middleReact refs — це спосіб отримати прямий доступ до DOM‑елементу або інстанції компонента. Використовують їх, коли потрібен фокус, вимірювання розмірів або інтеграція з сторонніми бібліотеками, що працюють без React. Не слід застосовувати refs для звичайного управління станом або логіки, оскільки це порушує декларативний підхід і ускладнює тестування.
Повне пояснення
Що це і навіщо
Refs (reference) у React – об’єкт, що зберігає посилання на DOM‑елемент або інстанцію класового компонента. Вони потрібні, коли треба виконати операції, які не піддаються декларативному опису: встановити фокус, виміряти розміри елемента, інтегрувати сторонні бібліотеки (наприклад, jQuery, D3), або викликати методи інстанції.
Ключові принципи
- createRef() – створює ref, який можна прив’язати до JSX‑атрибута
ref. - useRef() – хук, що повертає мутабельний об’єкт
{ current: ... }. - current – властивість, що зберігає реальний DOM‑елемент або інстанцію.
- Ref не змінюється при рендері, тому його можна використовувати як «постійний» посилання.
Як це працює
- Компонент створює ref (
const inputRef = useRef(null);). - У JSX передає
ref={inputRef}. - Після монтінгу React встановлює
inputRef.currentв реальний DOM‑елемент. - Компонент може звертатися до
inputRef.currentу колбеках, useEffect тощо.
Практика і реалізація
import { useRef, useEffect } from 'react';
function FocusInput() {
const input = useRef<HTMLInputElement>(null);
useEffect(() => input.current?.focus(), []);
return <input ref={input} />;
}
Тестування
Для тестів можна перевірити, що ref.current не null після рендеру:
const { getByRole } = render(<FocusInput />);
expect(getByRole('textbox')).toHaveFocus();
Коли не варто використовувати refs
- Управління станом: для зміни значень полів слід використовувати
useStateабо контекст. - Логіка рендеру: якщо можна описати поведінку декларативно, refs не потрібні.
- Тестованість: прямий доступ до DOM ускладнює ізоляцію компоненту.
Переваги та недоліки
- Перевага: простий спосіб взаємодії з DOM‑елементами.
- Недолік: порушує декларативний підхід, може призвести до непередбачуваних побічних ефектів.
Альтернативи
- Controlled components: управління значенням через
value/onChange. - React Hook Form: для форм без refs.
Чеклист діагностики
| Симптом | Причина | Дія |
|---|---|---|
| Фокус не встановлюється | Ref не прив’язаний або current null | Перевірити JSX‑атрибут ref і порядок рендеру |
| Вимірювання розмірів некоректне | Доступ до current перед монтінгом | Перенести логіку у useEffect з порожнім масивом залежностей |
| Бібліотека не працює | Ref не передається в потрібний елемент | Переконатися, що ref прив’язаний до коректного DOM‑елемента |
Часті помилки
- Використання
refу функціональному компоненті безuseRef()– створює новий об’єкт при кожному рендері. - Заміна
refнаuseRef(null)у класовому компоненті – слід використовуватиcreateRef(). - Забудьте про очищення – при демонтажі компоненту
currentстає undefined, тому слід перевіряти. - Використання
refдля управління станом – краще використовуватиuseState. - Неправильний тип – у TypeScript треба вказати правильний тип елемента (
HTMLInputElement,HTMLDivElementтощо). - Виклик методу до монтінгу –
currentбуде null. - Використання
refу списку елементів без ключа – може призвести до неправильного прив’язування. - Зміна
refу циклі – не рекомендується, бо створює новий об’єкт.
Cheatsheet
const ref = useRef(null);– створити ref.<div ref={ref}>– прив’язати.ref.current?.focus();– встановити фокус.useEffect(() => { /* після монтінгу */ }, []);– доступ до DOM.
Follow‑up питання
- Як можна об’єднати refs з
useCallbackдля оптимізації? - Чому використання refs у класових компонентах відрізняється?
- Які альтернативи існують для інтеграції D3 у React без refs?