> В чем отличие useEffect от useLayoutEffect в React (React)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: Точка, Soft_Media_Group, EvApps, VK, Aston, IT-One, Софт Медиа Групп, EPAM, ПСБ, intelsy, Black Wall Group (BWG), Сбер

Стек: React

> Пример ответа

Короткий ответ

Основное отличие - время выполнения. useEffect запускается асинхронно после того, React отрисовал DOM и браузер выполнил paint. useLayoutEffect запускается синхронно сразу после обновления DOM, но до того, как браузер показал изменения пользователю. Это делает useLayoutEffect подходящим для операций, которые должны быть выполнены до отрисовки, например, чтение layout или синхронные мутации DOM.

Подробное объяснение

Оба хука принимают функцию с эффектом и массив зависимостей. Разница в фазе выполнения в React lifecycle:

  • useEffect: выполняется после того, как React зафиксировал изменения в DOM и браузер отобразил их (commit phase → paint). Это асинхронная операция, не блокирующая визуализацию. Подходит для побочных эффектов, не влияющих на layout: запросы к API, подписки на события, логирование.

  • useLayoutEffect: выполняется синхронно сразу после того, как React обновил DOM, но до того, как браузер выполнил paint. Это блокирует отрисовку до завершения эффекта. Подходит для операций, которые должны быть выполнены до того, как пользователь увидит изменения: измерение размеров элементов, синхронное изменение стилей, анимации на основе layout.

React гарантирует, что все useLayoutEffect из одного дерева компонентов выполнятся последовательно, прежде чем браузер сможет отрисовать кадр. Это может привести к задержкам при рендеринге, если эффект тяжёлый.

На практике

  • Используйте useEffect по умолчанию для большинства побочных эффектов. Это безопаснее и не блокирует UI.
  • Используйте useLayoutEffect только когда вам нужно:
    • Прочитать layout элемента (например, getBoundingClientRect, offsetHeight) и сразу применить изменения, чтобы избежать визуального мерцания.
    • Синхронно обновить DOM перед отрисовкой (например, позиционирование тултипа, скролл к элементу).
    • Выполнить эффект, который должен быть завершён до того, как пользователь увидит результат (например, анимация входа).

Если вы используете useLayoutEffect на сервере (SSR), React выдаст предупреждение, так как на сервере нет браузерного API. В таких случаях нужно либо переключиться на useEffect, либо использовать useIsomorphicLayoutEffect (кастомный хук, который выбирает useLayoutEffect на клиенте и useEffect на сервере).

Пример кода

JSX
import { useState, useRef, useEffect, useLayoutEffect } from 'react';
function Tooltip({ text }) {
const [tooltipWidth, setTooltipWidth] = useState(0);
const tooltipRef = useRef(null);
// useLayoutEffect - читаем layout до отрисовки
useLayoutEffect(() => {
if (tooltipRef.current) {
const width = tooltipRef.current.offsetWidth;
setTooltipWidth(width);
}
}, [text]);
// useEffect - подписка на событие после отрисовки
useEffect(() => {
const handleResize = () => console.log('Window resized');
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return (
<div
ref={tooltipRef}
style={{ position: 'absolute', left: `calc(50% - ${tooltipWidth / 2}px)` }}
>
{text}
</div>
);
}

Как отвечать на собеседовании

Начните с ключевого отличия: время выполнения относительно paint. Приведите конкретный сценарий, где useLayoutEffect необходим (например, измерение DOM), и объясните, почему useEffect не подходит (будет мерцание). Упомяните, что useLayoutEffect блокирует отрисовку, поэтому его следует использовать только при необходимости. Если спросят про SSR - скажите про useIsomorphicLayoutEffect. Держите ответ структурированным: сначала разница, потом когда что применять, потом пример.

Что проверяет интервьюер

  • Понимание React lifecycle и фазы commit.
  • Умение выбирать правильный хук под задачу.
  • Осознание trade-off между производительностью и корректностью UI.
  • Знание ограничений (SSR, блокировка paint).
  • Способность объяснить разницу простыми словами и с примерами.

Типичные ошибки

  • Использовать useLayoutEffect для всех эффектов "на всякий случай" - это ухудшает производительность.
  • Путать порядок выполнения: useLayoutEffect выполняется до useEffect в одном компоненте.
  • Пытаться читать layout в useEffect и обновлять состояние - это вызывает дополнительный рендер и мерцание.
  • Не учитывать SSR: useLayoutEffect вызовет warning на сервере.
  • Думать, что useLayoutEffect выполняется до монтирования компонента - он выполняется после монтирования, но до paint.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью