React-додатки будуються з компонентів, і кожен компонент проживає своє життя. Він з'являється на екрані. Реагує на нову інформацію. Зрештою — зникає. Ігноруйте цей ритм — і ціна буде відчутною. Починаються витоки пам'яті. Продуктивність рендерингу падає. Користувачі натискають кнопки, які більше не реагують, тому що компонент, прикріплений до них, був розмонтований десять хвилин тому, але слухач подій (event listener) все ще живий. Розуміння життєвого циклу компонента — це не академічна вправа. Це фундамент написання програмного забезпечення, яке залишається надійним під час реального використання.
Уявіть існування компонента у трьох стадіях. Розробники часто описують їх як народження, ріст і смерть. React називає їх монтажем (mounting), оновленням (updating) та розмонтуванням (unmounting). Кожна стадія має своє конкретне завдання, і кожна дає вам точний момент для виконання коду або його очищення.
Три фази
Mounting (монтаж) — це народження. Компонент створюється та вперше вставляється в DOM браузера. Це ваше вікно налаштування. Якщо компоненту потрібні віддалені дані, саме тут ви робите запит. Якщо йому потрібен початковий стан (state), розрахований на основі пропсів (props), ви робите це тут. В епоху класових компонентів це була сфера componentDidMount. У сучасних функціональних компонентах ви обробляєте це за допомогою useEffect та порожнього масиву залежностей. Ключова деталь полягає в тому, що цей блок коду виконується один раз, одразу після того, як React розміщує елемент на сторінці. Це робить його ідеальним для разового налаштування, наприклад, отримання профілю користувача або читання значення з localStorage.
Updating (оновлення) — це ріст. Розмонтований (mounted) компонент не стоїть на місці. Приходять пропси від батьківського компонента. Змінюється стан через взаємодію з користувачем. Сам батьківський компонент перерендериться, змушуючи дитину слідувати за ним. Потім React перерендерить компонент, щоб підтримувати інтерфейс у синхронізації з вашими даними. Ця фаза повторюється багато разів протягом життя компонента. Щоразу, коли змінюється залежність, ваші ефекти запускаються знову, і компонент вдихає новий результат назад у DOM. Оскільки ця фаза спрацьовує так часто, саме тут зазвичай виникає більшість проблем із продуктивністю. Зайва робота на цьому етапі швидко накопичується.
Unmounting (розмонтування) — це смерть. Компонент зникає з екрана, можливо, тому, що користувач перейшов на іншу сторінку або умовний рендеринг (conditional render) вимкнувся. У цей момент компонент знищується, але зовнішній світ не завжди про це знає. Таймери продовжують іти. WebSockets продовжують слухати. Слухачі подій на об'єкті window залишаються прикріпленими. Якщо ви не очистите їх, вони триматимуть посилання на мертві компоненти та спричинятимуть витоки пам'яті. Браузер витрачатиме ресурси на «привидів». React надає вам функцію очищення (cleanup function) саме для того, щоб ви могли зупинити інтервали, закрити з'єднання та видалити слухачів перед тим, як компонент зникне назавжди.
Від методів класів до хуків
Роками інженери React керували цими фазами через класові компоненти. Логіка була розділена між окремими методами. Ви отримували дані всередині componentDidMount, реагували на зміни пропсів у componentDidUpdate і «підмітали підлогу» всередині componentWillUnmount. Проблема полягала в тому, що пов'язана логіка опинялася розкиданою. Одна функція, наприклад, підключення до чату в реальному часі, потребувала коду в трьох окремих методах життєвого циклу лише для того, щоб підключитися, перевірити зміни в потоці та відключитися.
Хуки змінили це. Хук useEffect у функціональних компонентах дозволяє групувати логіку за призначенням, а не за фазою життєвого циклу. Проте ментальна модель чітко співвідноситься, якщо ви знаєте, куди дивитися.
Щоб обробити монтаж, напишіть useEffect(() => { ... }, []). Порожній масив залежностей каже React, що цей ефект не має зовнішніх залежностей, тому він запускається лише після початкового рендерингу. Це замінює componentDidMount.
Щоб обробити оновлення, додайте конкретні значення, за якими React має стежити: useEffect(() => { ... }, [dependency]). Коли dependency змінюється між рендерингами,
