У розробці додатків на React ефективне керування посиланнями (refs) є важливим для створення інтерактивних та чутливих компонентів. Посилання дозволяють розробникам безпосередньо отримувати доступ до елементів DOM або екземплярів компонентів, надаючи більш детальне керування взаємодією компонентів та їх поведінкою. У цій статті ми розглянемо, як використовувати useRef
, forwardRef
і useImperativeHandle
для ефективного керування посиланнями, вирішуючи типові проблеми комунікації між батьківським і дочірнім компонентами.
Короткий опис призначення кожного:
- useRef:
useRef
— це хук, який дозволяє зберігати змінні значення, що зберігаються між рендерами. Це ідеальний інструмент для прямого доступу до елементів DOM, наприклад, для фокусування на полі вводу при натисканні кнопки. - forwardRef:
forwardRef
— це вища функція, яка дозволяє передавати посилання з батьківського компонента до дочірнього. Це забезпечує безпосередню взаємодію з внутрішніми елементами дочірнього компонента, особливо у функціональних компонентах. - useImperativeHandle:
useImperativeHandle
дозволяє налаштувати, які методи або властивості будуть доступні через посилання. Це дає більше точного контролю над поведінкою компонента, дозволяючи батьківському компоненту отримувати доступ до конкретних методів дочірнього компонента.
Важлива примітка: З виходом React 19 використання forwardRef
більше не є необхідним. Тепер можна передавати ref
як пропс безпосередньо в функціональні компоненти. forwardRef
буде знято в майбутніх версіях. Детальніше можна прочитати тут.
Практичне завдання: Керування фокусом між батьківським і дочірнім компонентами
Щоб продемонструвати, в яких випадках корисно використовувати посилання (refs) у React, давайте розглянемо рішення досить простої задачі. Метою є показати, як батьківський компонент може ефективно взаємодіяти з дочірнім компонентом. У наступному прикладі кнопка в батьківському компоненті перемикає фокус між двома полями вводу, що знаходяться в дочірньому компоненті.
Цей сценарій є типовым для додатків, де взаємодія з користувачем вимагає перемикання фокусу між різними елементами вводу без необхідності повторного рендерингу компонентів або передачі непотрібних станів.
Для того, щоб зберегти основну мету, очікувана поведінка буде такою, як показано у відео нижче:
Перш за все, давайте визначимо інтерфейс, який дозволить дочірньому компоненту надавати методи, які будуть використовуватися батьківським компонентом.
interface InputsContainerType {
setInput1: () => void;
setInput2: () => void;
}
Тепер давайте створимо компонент, який буде служити обгорткою для полів вводу. Цей компонент, названий InputsContainer
, використовує forwardRef
, щоб дозволити батьківському компоненту передавати посилання, яке можна використовувати для керування фокусом на полях вводу. Додатково ми будемо використовувати Tailwind CSS для побудови та стилізації елементів, щоб забезпечити чистий та чутливий інтерфейс.
const InputsContainer = forwardRef((_, ref) => {
const input1Ref = useRef(null);
const input2Ref = useRef(null);
useImperativeHandle(ref, () => ({
setInput1: () => {
input1Ref.current?.focus();
},
setInput2: () => {
input2Ref.current?.focus();
},
}));
return (
);
});
InputsContainer.displayName = "InputsContainer";
Щоб зрозуміти, як працює код, давайте детально розглянемо роль кожного з використаних хуків.
- forwardRef: Використовується для того, щоб дозволити батьківському компоненту передавати посилання до дочірнього компонента. Це необхідно, щоб батьківський компонент міг взаємодіяти безпосередньо з внутрішніми елементами дочірнього компонента.
ВикористовуючиforwardRef
, дочірній компонент може отримати посилання (ref) і надати конкретні методи або значення батьківському компоненту, що полегшує комунікацію між ними. - useRef: Створює посилання для елементів вводу (
input1Ref
таinput2Ref
), що дозволяє безпосередньо звертатися до елементів DOM. Ці посилання використовуються для керування фокусом на полях вводу.useRef
ідеально підходить для зберігання змінних значень, що не спричиняють повторний рендеринг при зміні, таких як посилання на елементи DOM. - useImperativeHandle: Надає методи (
setInput1
таsetInput2
), що дозволяють батьківському компоненту фокусуватися на конкретних полях вводу. Це досягається шляхом інкапсуляції логіки фокуса в дочірньому компоненті, але з можливістю керувати цією логікою з боку батьківського компонента.useImperativeHandle
використовується разом зforwardRef
, щоб налаштувати інтерфейс, який надається дочірнім компонентом, роблячи його більш гнучким і повторно використовуваним.
Щоб створити батьківський компонент Home
, ми будемо слідувати покроковому підходу, починаючи з керування станом та посиланнями (refs), створення контрольних функцій і, зрештою, рендерингу компонента.
1. Керування станом та посиланнями
const [inputNumber, setInputNumber] = useState(1);
const inputRef = useRef(null);
- useState: Використовується для керування станом
inputNumber
, що визначає, яке поле вводу має бути фокусовано. Спочатку стан встановлюється в значення1
, що вказує на те, що перше поле вводу буде фокусовано за замовчуванням. - useRef: Створює посилання (
inputRef
), яке передається вInputsContainer
. Це посилання дозволяє батьківському компоненту викликати методи, що надаються дочірнім компонентом, для фокусування на полях вводу.
2. Створення функції для перемикання фокусу
const switchFocusState = () => {
setInputNumber((prev) => (prev === 1 ? 2 : 1));
};
- Функція
switchFocusState
: Ця функція відповідає за перемикання значенняinputNumber
між 1 і 2. При виклику вона змінює фокус між двома полями вводу, дозволяючи користувачеві взаємодіяти з ними по черзі.
3. Додавання події при зміні inputNumber
useEffect(() => {
if (inputNumber === 1) {
inputRef.current?.setInput1();
return;
}
inputRef.current?.setInput2();
}, [inputNumber]);
- useEffect: Ми використовуємо хук
useEffect
для моніторингу змін у станіinputNumber
. Кожного разу, коли стан змінюється, ефект викликає відповідний метод (setInput1
абоsetInput2
) наInputsContainer
для фокусування на відповідному полі вводу. Це гарантує, що фокус оновлюється відповідно до поточного стану.
4. Рендеринг компонента
return (
set focus on input number: {inputNumber === 1 ? 2 : 1}
);
- Кнопка перемикання: Ми рендеримо кнопку, яка, при натисканні, викликає функцію
switchFocusState
, щоб перемикати фокус між полями вводу.
Текст кнопки вказує, яке поле вводу буде фокусуватися при натисканні. - InputsContainer: Компонент
InputsContainer
рендериться з посиланнямinputRef
, що дозволяє батьківському компоненту керувати фокусом на полях вводу.
Ось повний код для компонента Home
:
export default function Home() {
const [inputNumber, setInputNumber] = useState(1);
const inputRef = useRef(null);
const switchFocusStae = () => {
setInputNumber((prev) => (prev == 1 ? 2 : 1));
};
useEffect(() => {
if (inputNumber === 1) {
inputRef.current?.setInput1();
return;
}
inputRef.current?.setInput2();
}, [inputNumber]);
return (
set focus on input number: {inputNumber == 1 ? 2 : 1}
);
}
Висновок
У цій статті ми розглянули, як використовувати useRef
, forwardRef
та useImperativeHandle
для керування посиланнями (refs) і комунікацією між батьківським і дочірнім компонентами в React. Через практичний приклад ми продемонстрували, як ці інструменти можна використовувати для вирішення поширених проблем взаємодії між компонентами, таких як перемикання фокусу між полями вводу. Цей підхід дозволяє батьківському компоненту безпосередньо контролювати поведінку елементів у дочірньому компоненті, що забезпечує зручну і ефективну взаємодію з користувачем.
Застосовуючи ці техніки, розробники можуть створювати більш інтерактивні та чуйні компоненти React, покращуючи досвід користувача та ефективність коду. Розуміння та застосування цих концепцій є важливим для будь-якого розробника, який хоче максимально використати потенціал своїх React додатків, особливо в сценаріях, де необхідна пряма маніпуляція DOM. Опанувавши ці хуки, ви будете готові до вирішення складних задач розробки в React, що забезпечить надійність, масштабованість та легкість в обслуговуванні ваших додатків.
Перекладено з: Boost Your React Application’s Performance: The Power of Properly Used Refs