Чем отличается useEffect от useLayoutEffect?
Хуки useEffect и useLayoutEffect в React (и, соответственно, в React Native) используются для управления побочными эффектами внутри функциональных компонентов. Несмотря на то, что оба выполняют схожие задачи — позволяют выполнять код после рендера компонента — между ними есть ключевые различия во времени выполнения, влиянии на рендеринг, а также в типичных сценариях применения.
Общее понимание "эффектов" в React
Функциональные компоненты в React изначально не имели доступа к жизненному циклу компонентов, как в классовом API (componentDidMount, componentDidUpdate, componentWillUnmount). Хуки useEffect и useLayoutEffect добавлены для управления побочными действиями: подписками, запросами к API, анимациями, логированием, обновлением DOM и т. д.
Как работают useEffect и useLayoutEffect
useEffect
-
Выполняется после того, как изменения отрисованы на экране (DOM обновлён).
-
Работает асинхронно, не блокирует отрисовку интерфейса.
-
Подходит для: запросов к серверу, логирования, синхронизации данных, таймеров, подписок.
useEffect(() => {
console.log('Сработал useEffect');
}, \[\]);
useLayoutEffect
-
Выполняется синхронно сразу после рендера, но до того, как браузер отобразит изменения пользователю.
-
Позволяет измерять DOM и синхронно влиять на него до того, как пользователь его увидит.
-
Блокирует отрисовку, пока эффект не завершится.
-
Подходит для: точного измерения размеров элементов, принудительного скролла, анимаций на основе геометрии, позиционирования.
useLayoutEffect(() => {
console.log('Сработал useLayoutEffect');
}, \[\]);
Сравнение по ключевым параметрам
Параметр | useEffect | useLayoutEffect |
---|---|---|
Время выполнения | После отрисовки на экране | После рендера, но до отображения |
--- | --- | --- |
Синхронность | Асинхронный | Синхронный |
--- | --- | --- |
Блокирует ли рендер | ❌ Нет | ✅ Да |
--- | --- | --- |
Подходит для работы с DOM | Только если не важна точность тайминга | Для точных измерений, скроллов, позиций |
--- | --- | --- |
Производительность | Лучше | Медленнее, может тормозить интерфейс |
--- | --- | --- |
Основные сценарии | API-запросы, подписки, логирование | Прокрутка, измерения, UI-фиксы |
--- | --- | --- |
Пример различий
Сценарий: измерение ширины элемента и установка ширины другого элемента
const MyComponent = () => {
const refA = useRef();
const refB = useRef();
useEffect(() => {
const width = refA.current.offsetWidth;
refB.current.style.width = \`${width}px\`; // может "прыгнуть"
}, \[\]);
return (
<View>
<Text ref={refA}>Измеряемый текст</Text>
<Text ref={refB}>Растягиваемый текст</Text>
</View>
);
};
Здесь useEffect выполнится после того, как refA уже отрисован и пользователь может увидеть короткий момент, когда refB ещё не растянут. Это вызовет визуальный скачок (layout shift).
Исправление через useLayoutEffect
useLayoutEffect(() => {
const width = refA.current.offsetWidth;
refB.current.style.width = \`${width}px\`;
}, \[\]);
В этом случае refB получит нужную ширину до того, как произойдёт отрисовка, и пользователь увидит уже готовый результат.
Типичные случаи использования useEffect
-
Получение данных с сервера (fetch, axios)
-
Настройка подписок (например, WebSocket, события клавиатуры)
-
Установка таймеров, интервалов
-
Взаимодействие с AsyncStorage, Location, Permissions
-
Изменение глобального состояния (через Redux, Recoil, Context)
useEffect(() => {
const timer = setInterval(() => {
console.log('Тик');
}, 1000);
return () => clearInterval(timer); // очистка
}, \[\]);
Типичные случаи использования useLayoutEffect
-
Измерение размеров DOM-элементов
-
Программная прокрутка (scroll) до нужного элемента
-
Применение стилей до рендера (например, inline-стили для анимаций)
-
Управление фокусом (например, inputRef.focus() перед показом)
-
Тонкая настройка UI перед отрисовкой
useLayoutEffect(() => {
const { height } = someRef.current.getBoundingClientRect();
console.log('Высота элемента:', height);
}, \[\]);
Предупреждение о useLayoutEffect в SSR
React выдаёт предупреждение при использовании useLayoutEffect в среде серверного рендеринга (например, в Next.js), потому что на сервере нет DOM, и этот хук не имеет смысла. Альтернатива — использовать useEffect с проверкой окружения:
const isBrowser = typeof window !== 'undefined';
if (isBrowser) {
useLayoutEffect(() => {
// только в браузере
}, \[\]);
}
Совмещение useEffect и useLayoutEffect
В сложных компонентах можно использовать оба хука:
useLayoutEffect(() => {
// измерения или подготовка DOM
}, \[\]);
useEffect(() => {
// подписки, запросы, side-effects
}, \[\]);
Важно понимать, что useLayoutEffect всегда срабатывает раньше, чем useEffect.
React Native и layout-эффекты
В React Native также работает useLayoutEffect, но вместо DOM используется Yoga layout engine. Его полезно применять, если нужно измерить View, задать скролл в ScrollView, отобразить модальное окно после точного позиционирования и т. д.
Пример прокрутки до нижней части ScrollView после появления контента:
useLayoutEffect(() => {
scrollViewRef.current.scrollToEnd({ animated: true });
}, \[messages\]);
Основные различия с точки зрения разработки
Аспект | useEffect | useLayoutEffect |
---|---|---|
Безопасность для SSR | ✅ безопасен | ❌ может вызвать ошибку при рендере на сервере |
--- | --- | --- |
Порядок вызова | После отрисовки | До отрисовки |
--- | --- | --- |
Видимость пользователем | Пользователь может увидеть промежуточный результат | Пользователь увидит только итоговый результат |
--- | --- | --- |
Побочные эффекты (анимации, размеры) | ❌ может привести к визуальным скачкам | ✅ полностью под контролем |
--- | --- | --- |
Когда использовать какой хук
- **Вы используете setTimeout, fetch, console.log и подписки → useEffect
** - **Нужно измерить размер, установить фокус или прокрутку → useLayoutEffect
** - **Появляются "прыгающие" элементы (layout shift) → использовать useLayoutEffect
** - **Работа в SSR-среде → избегать useLayoutEffect
**
Оба хука важны в арсенале разработчика на React и React Native. Понимание того, в каком порядке и в каком контексте они выполняются, критично для создания производительных, отзывчивых и визуально стабильных интерфейсов.