Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Чому useEffect не гарантовано викликається одразу після commit?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**`useEffect`** не викликається одразу після commit, тому що це пасивний ефект, який виконується асинхронно, після того як браузер завершив перемальовування (paint). **Ключове:** якщо потрібен ефект синхронно одразу після оновлення DOM, використовують `useLayoutEffect`, а не `useEffect`.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення## 1. Що робить commit-фаза У React кожен цикл оновлення ділиться на дві великі частини: | Фаза | Що робить | Можна перервати? | |---|---|---| | **Render Phase** | React обчислює, що потрібно змінити (нове Virtual DOM) | Так | | **Commit Phase** | React застосовує зміни в реальний DOM | Ні | Після **commit-фази** DOM вже оновлений, екран перемальований, і React готовий викликати ефекти (`useEffect`, `useLayoutEffect`, `componentDidMount` тощо). Але, важливо, не всі ефекти однакові за пріоритетом. --- ## 2. Різні типи ефектів React розрізняє два типи побічних ефектів: | Тип | Хук | Коли викликається | |---|---|---| | **Синхронний (layout)** | `useLayoutEffect` | **одразу після commit**, *до* відображення на екрані | | **Пасивний (passive)** | `useEffect` | **асинхронно, після відображення і браузерного paint-тіку** | --- ## 3. Чому `useEffect()` - **асинхронний** Після commit React: 1. Оновлює DOM. 2. Повідомляє браузеру: "готово, можна перемалювати екран". 3. **Чекає**, поки браузер закінчить paint. 4. Тільки потім викликає всі "пасивні" ефекти (`useEffect`). Це потрібно, щоб: - UI став **видимим якомога швидше**; - ефекти (мережеві запити, таймери, підписки тощо) **не гальмували перемальовування**; - браузер залишався чутливим (scroll, input, animation не блокуються). --- ## Приклад (різниця між `useLayoutEffect` і `useEffect`) ```javascript useLayoutEffect(() => { console.log('useLayoutEffect'); // синхронно після оновлення DOM }); useEffect(() => { console.log('useEffect'); // асинхронно, після paint }); ``` Лог у консолі: ```javascript render commit useLayoutEffect ← одразу після commit PAINT (браузер) useEffect ← після відображення ``` --- ## 4. Чому це важливо в React 18 і concurrent rendering У React 18 рендер став **переривним і пріоритетним** (Concurrent Rendering). Через це React може: - відкладати виклик `useEffect()`, якщо є більш термінові завдання (наприклад, користувач клікнув); - **скасовувати** ефекти, якщо рендер скасувався до commit; - **об'єднувати** кілька рендерів в один commit, а ефекти викликати тільки один раз після нього. Тому React *гарантує лише одне*: `useEffect()` буде викликаний **після того, як DOM вже оновлений і браузер показав зміни**, але **не обов'язково миттєво** - це може бути на наступному event loop-тіку. --- ## 5. А якщо потрібен ефект "одразу після commit"? Тоді потрібно використовувати `useLayoutEffect()`: ```javascript useLayoutEffect(() => { console.log('Синхронно після commit'); }); ``` `useLayoutEffect`: - викликається синхронно одразу після того, як React оновив DOM; - блокує відображення до завершення колбека; - ідеально підходить для вимірювань (наприклад, `getBoundingClientRect`). > Але використовувати `useLayoutEffect` без потреби небезпечно, > він може **затримувати відображення UI**. > Для більшості побічних ефектів (`fetch`, підписки, таймери) краще `useEffect`. --- ## 6. Візуально ```javascript Render phase ↓ Commit phase (DOM оновлено) ↓ → useLayoutEffect (одразу) ↓ Браузер відобразив (paint) ↓ → useEffect (асинхронно) ``` --- ## 7. Підсумок > React **не викликає** `useEffect` **одразу після commit**, > тому що `useEffect` - це **пасивний ефект**, який виконується > **асинхронно після того, як браузер завершив paint**. > > Це гарантує, що: > > - UI відобразиться максимально швидко, > - ефекти не блокують інтерфейс, > - concurrent rendering залишається чутливим. --- | Хук | Коли викликається | Використовувати для | |---|---|---| | `useLayoutEffect` | одразу після commit (синхронно) | вимірювання DOM, синхронізація layout | | `useEffect` | після paint (асинхронно) | побічні ефекти, запити, підписки |Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.