Що робить хук useInsertionEffect()?
Коротко: що робить useInsertionEffect()
useInsertionEffect() - це хук, який виконується синхронно перед тим, як браузер застосує стилі та виконає layout, тобто ще раніше, ніж useLayoutEffect().
Він потрібен, щоб бібліотеки могли:
- вставити CSS-правила у DOM до того, як браузер намалює сторінку;
- уникнути «мерехтіння» стилів (FOUC) під час динамічної генерації CSS;
- гарантувати узгодженість стилів за concurrent rendering (React 18).
Синтаксис
useInsertionEffect(() => {
// synchronous insertion of styles or other side effects, before layout
}, [deps]);За інтерфейсом він схожий на useEffect і useLayoutEffect, але виконується раніше за всі ефекти React, буквально до того, як браузер відмалює екран.
1. Навіщо він потрібен (і кому)
До React 18 бібліотеки для CSS-in-JS (наприклад, Emotion, styled-components, JSS):
- генерували класи і вставляли стилі в
- використовували для цього useLayoutEffect().
Проблема:
У Concurrent Rendering React може почати рендер, призупинити його, а потім продовжити, що призводило до того, що стилі вставлялися невчасно або навіть кілька разів, створюючи артефакти й мерехтіння контенту.
useInsertionEffect() вирішує це: він виконується під час фази «commit», до layout і до відмальовування, тому стилі гарантовано вставлені, перш ніж браузер щось намалює.
2. Порядок виконання хуків
Ось порядок, у якому React викликає ефекти:
| Хук | Коли виконується | Основне призначення |
|---|---|---|
| useInsertionEffect | До layout і до paint | Вставка стилів / синхронні операції перед відмальовуванням |
| useLayoutEffect | Після DOM-коміту, але до paint | Вимірювання, позиціонування, синхронні зміни DOM |
| useEffect | Після paint (асинхронно) | Побічні ефекти, підписки, логіка |
Таким чином:
useInsertionEffect → useLayoutEffect → useEffect
3. Приклад (імітація роботи CSS-in-JS)
import { useInsertionEffect } from "react";
function DynamicStyle({ color }) {
useInsertionEffect(() => {
const style = document.createElement("style");
style.textContent = `.dynamic-text { color: ${color}; }`;
document.head.appendChild(style);
return () => {
document.head.removeChild(style);
};
}, [color]);
return <p className="dynamic-text">Hello, world!</p>;
}Тут:
- стиль вставляється в до того, як React намалює DOM;
- користувач ніколи не бачить «нефарбований» текст;
- при зміні кольору старі стилі видаляються.
4. Різниця з іншими ефектами
| Хук | Час виконання | Чи може викликати reflow/paint? | Використовувати для |
|---|---|---|---|
| useEffect | Після відмальовування | може | Підписки, запити, side-effects |
| useLayoutEffect | Після DOM-коміту, до paint | може викликати reflow | Вимірювання, DOM-маніпуляції |
| useInsertionEffect | До layout, до paint | ні | Вставка стилів, бібліотечні ефекти |
5. Особливості
Ви не повинні використовувати useInsertionEffect у звичайних застосунках. Цей хук призначений лише для бібліотек, які вставляють CSS синхронно.
React гарантує, що ефекти useInsertionEffect виконуються в порядку ієрархії дерева компонентів - це запобігає дублюванню або гонкам під час генерації класів.
Не можна читати layout або DOM усередині useInsertionEffect. Його завдання - вставка, а не вимірювання. Для вимірювань використовуй useLayoutEffect.
6. Приклад реального застосування (Emotion)
useInsertionEffect(() => {
const sheet = document.head.querySelector('#emotion-styles');
sheet.insertRule(`.css-${hash} { color: ${color}; }`);
}, [hash, color]);Ця логіка дозволяє бібліотеці вставляти CSS «на льоту» до рендера браузера, без мерехтіння й артефактів під час переходів.
7. Підсумок
useInsertionEffect() - це низькорівневий, синхронний хук, який виконується ще до layout і paint, щоб бібліотеки могли вставити динамічні стилі в DOM вчасно, забезпечуючи стабільний зовнішній вигляд навіть за конкурентного рендерингу.
Коротко
| Властивість | Значення |
|---|---|
| Виконується | До useLayoutEffect і useEffect |
| Сумісність із SSR | Так |
| Для кого призначений | Бібліотеки CSS-in-JS (Emotion, styled-components, JSS та ін.) |
| Чи можна читати DOM? | Ні |
| Чи можна змінювати DOM (наприклад, вставити | Так |
| Працює за concurrent rendering | Так |
| Підтримується з | React 18+ |
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.