У React refs — це спосіб безпосереднього звернення до елементів DOM або інстанцій компонентів. Вони зазвичай використовуються для взаємодії з елементами DOM поза межами потоку даних React, такими як фокусування на інпуті або запуск анімацій. Однак React не дозволяє передавати refs як звичайні пропси, оскільки refs надають прямий доступ до DOM-елемента дитини, що може порушити декларативну природу React.
forwardRef стає необхідним, коли ви хочете передати ref з батьківського компонента до дочірнього. Без forwardRef, батьківський компонент не може безпосередньо отримати доступ до DOM-елемента або інстанції дитини. Це особливо корисно, коли працюєте з кастомними компонентами, які повинні відкривати свій внутрішній DOM для батька, не порушуючи односторонній потік даних React.
Але що робити, якщо ref потрібно передавати через кілька рівнів компонентів? У цьому випадку кожен компонент у ланцюгу має використовувати forwardRef. Наприклад, якщо батьківський компонент хоче передати ref до дочірнього компонента 2, минаючи дочірній компонент 1, весь ланцюг компонентів має передавати ref.
import React, { useRef, forwardRef } from 'react';
// Дитина 2
const Child2 = forwardRef((props, ref) => {
return ;
});
// Дитина 1 (передає ref до Дитини 2)
const Child1 = forwardRef((props, ref) => {
return (
Дитина 1
); }); // Батьківський компонент function Parent() { const child2Ref = useRef(); // Ref створено в батьку return (
Батьківський компонент
); } export default Parent; ``` ## Чому використовувати useImperativeHandle в React? Сценарій з модулярною федерацією У сучасних додатках React, особливо тих, що використовують модульну федерацію, управління взаємодією компонентів через різні репозиторії може бути складним. Давайте розглянемо практичний сценарій, що демонструє корисність хука **_useImperativeHandle_**. Уявімо додаток, організований через кілька репозиторіїв для підтримки розподілу обов’язків за допомогою архітектури модульної федерації: • **Репозиторій хоста**: відповідає за розкладку сторінки та загальну структуру додатка. • **Репозиторій віддаленого компонента**: відповідає за створення окремих компонентів та панелей. **Задача: управління даними форми** Розглянемо процес створення акаунту користувача, де: • Форма знаходиться в одному компоненті. • Дії форми (створення/скасування) знаходяться в окремому закріпленому хедері. • Ці компоненти знаходяться в різних репозиторіях. **Можливі рішення** **Рішення 1: Управління станом через пропси** Це рішення передбачає передачу всіх даних форми через пропси від батька (хоста) до дитини (віддаленого). Однак у великих проєктах цей підхід стає незручним, оскільки збільшується складність і створюється зайвий потік даних. **Рішення 2: Глобальне управління станом** Використання **_Redux_** або **_React Context_** для управління глобальним станом дозволяє як хосту, так і віддаленим репозиторіям доступ до даних форми. Хоча це може працювати, воно вводить тісну зв’язку між двома репозиторіями, що компрометує модульну архітектуру, роблячи компоненти менш придатними для повторного використання. **Рішення 3: Підхід через useImperativeHandle** Хук **_useImperativeHandle_** пропонує чистий і декуплінгований спосіб комунікації між батьківським і дочірнім компонентами.
Це дозволяє дочірньому компоненту надавати конкретні методи батьківському компоненту, зберігаючи незалежність компонентів.
**Основні переваги _useImperativeHandle_:**
• **Мінімальне спільне використання стану**: Зменшує потребу в обміні станом між компонентами.
• **Збереження модульності компонентів**: Компоненти залишаються незалежними та придатними для повторного використання.
• **Точний контроль**: Дає батьківському компоненту контроль над тим, які методи надає дитина.
• **Безперебійна робота через межі модульної федерації**: Допомагає підтримувати чітке розділення між хостом і віддаленими репозиторіями, забезпечуючи при цьому комунікацію.
**Приклад реалізації-**
// У віддаленому репозиторії (CreateAccount.jsx)
import React, { useRef, useImperativeHandle, forwardRef, useState } from 'react';
const CreateAccountForm = forwardRef((props, ref) => {
const [formData, setFormData] = useState({});
useImperativeHandle(ref, () => ({
getFormData: () => formData,
validateForm: () => {
// Логіка валідації
return formData.isValid;
}
}));
return (
// Реалізація форми
);
});
// У репозиторії хоста
const AccountCreationPage = () => {
const formRef = useRef(null);
const handleCreateAccount = () => {
const formData = formRef.current.getFormData();
const isValid = formRef.current.validateForm();
if (isValid) {
// Продовжити створення акаунту
}
};
return (
<>
);
};
Перекладено з: Unraveling the Mysteries of forwardRef and useImperativeHandle