Как избежать перерасчёта компоновки и визуального мерцания
Определяйте, когда useEffect вызывает заметное мерцание, и заменяйте его на useLayoutEffect
«Как избежать перерасчёта компоновки и визуального мерцания» — бесплатный урок React Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения React Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс React Academy содержит 4 уроков всего.
Что такое каскадный перерасчёт компоновки
Каскадный перерасчёт компоновки происходит, когда JavaScript в цикле чередует чтение свойств DOM и запись в DOM. Каждое чтение после записи заставляет браузер синхронно пересчитывать компоновку, чтобы вернуть точное значение. Это может превратить один расчёт компоновки в десятки, серьёзно ухудшив производительность.
Как пакетная обработка обновлений помогает
React объединяет все вызовы setState внутри обработчиков событий и эффектов в один повторный рендеринг. Такая естественная пакетная обработка предотвращает шаблон «чтение — запись — чтение», вызывающий каскадный перерасчёт в одном цикле рендеринга. В React 18 пакетная обработка также распространяется на асинхронные обратные вызовы и обещания, уменьшая каскадные перерасчёты из-за одновременных обновлений состояния.
Когда возникает мерцание в useEffect
Мерцание возникает, когда useEffect считывает DOM, обновляет состояние, вызывает повторный рендеринг, и браузер выполняет отрисовку дважды: сначала с исходным состоянием, а затем с исправленным. Первая отрисовка ненадолго появляется на экране, после чего заменяется второй. Особенно заметно это для эффектов, которые изменяют свойства компоновки, например высоту, ширину или положение.
Диагностика мерцания с помощью DevTools
Откройте Chrome DevTools, перейдите на вкладку отрисовки через меню с тремя точками в разделе «Дополнительные инструменты» и включите «Подсветку отрисовки». Области повторной отрисовки будут выделяться зелёным цветом. Если при первой отрисовке компонент мерцает, а затем сразу повторно отрисовывается в другом расположении, это подтверждает двойную отрисовку, вызванную обновлением состояния через useEffect.
Исправление мерцания переносом в useLayoutEffect
Перенесите считывание DOM и вызванные им обновления состояния из useEffect в useLayoutEffect. Теперь то же измерение и обновление состояния выполняются синхронно до отрисовки, React повторно отображает и фиксирует изменения в том же блоке, а браузер выполняет отрисовку только итогового правильного состояния. Зелёная подсветка в DevTools исчезает.
Шаблон однократной отрисовки
Шаблон без мерцания выглядит так: считайте DOM в useLayoutEffect, вызовите setState с результатом измерения, React синхронно выполнит повторный рендеринг, зафиксирует исправленный DOM, а браузер один раз отрисует итоговый результат. Это стандартный шаблон для позиционирования всплывающих подсказок, смещений закреплённых заголовков и настройки анимаций.
Корректные случаи применения useEffect
Подавляющее большинство эффектов следует помещать в useEffect: получение данных (вспышка загрузки допустима и ожидаема), подписки на события или потоки, ведение журналов и аналитика, таймеры, а также любые эффекты, которые сразу не влияют на видимую компоновку. Избыточное использование useLayoutEffect там, где достаточно useEffect, — это антипаттерн, ухудшающий производительность.
Корректные случаи применения useLayoutEffect
Используйте useLayoutEffect только для измерений DOM, которые влияют на визуальный результат: позиционирования всплывающих подсказок, логики на основе размеров элементов, восстановления положения прокрутки и настройки анимаций, считывающих исходные размеры. Явный признак необходимости переключения — заметная однофреймовая вспышка неправильной компоновки при первой отрисовке.
Сравнение useEffect и useLayoutEffect в коде
У этих двух хуков одинаковые сигнатуры: useLayoutEffect(callback, deps) и useEffect(callback, deps). Различается только момент выполнения функции обратного вызова. В коде их можно заменить друг на друга одним словом. Начните с useEffect и переходите на useLayoutEffect только после подтверждения мерцания.
Совместное использование обоих хуков в одном компоненте
Использовать оба хука в одном компоненте для разных задач вполне допустимо и распространено. Например, компонент диаграммы может применять useLayoutEffect, чтобы измерить ширину контейнера и синхронно задать viewBox для SVG, а затем использовать useEffect для асинхронного получения данных диаграммы. Каждый хук решает ту задачу, для которой важен именно его момент выполнения.
Проверка двойной отрисовки
Помимо подсветки отрисовки в DevTools, можно добавить в компонент счётчик в консоли, чтобы подсчитывать рендеринги. Если при первом монтировании компонент дважды записывает в журнал «отрисовка» — сначала с исходным состоянием, а затем с исправленным измерением, — это сценарий двойной отрисовки. Перенос измерения в useLayoutEffect должен сократить число записей до одного рендеринга после монтирования (удвоение в строгом режиме не учитывается).
Когда следует предпочесть useLayoutEffect вместо useEffect
В каком сценарии следует использовать useLayoutEffect вместо useEffect?
Итоги урока: избыточные перерасчёты компоновки и визуальное мерцание
Избыточные перерасчёты компоновки возникают из-за чередования чтения и записи DOM. Пакетная обработка React предотвращает большую часть таких перерасчётов во время рендеринга. Мерцание возникает, когда useEffect измеряет DOM и обновляет состояние, вызывая две отрисовки. Исправляйте мерцание, перенося измерения DOM в useLayoutEffect. Во всех остальных случаях используйте useEffect по умолчанию: для получения данных, подписок, ведения журналов и таймеров.
Часто задаваемые вопросы
Урок «Как избежать перерасчёта компоновки и визуального мерцания» бесплатный?
Да — полный текст урока «Как избежать перерасчёта компоновки и визуального мерцания» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс React Academy, подпишись на CoddyKit PRO. Курс React Academy содержит 4 уроков всего.
Чему я научусь в уроке «Как избежать перерасчёта компоновки и визуального мерцания»?
Определяйте, когда useEffect вызывает заметное мерцание, и заменяйте его на useLayoutEffect Ты практикуешь React Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать React Academy?
Предыдущий опыт не требуется. React Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.
Сколько времени занимает урок «Как избежать перерасчёта компоновки и визуального мерцания»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке React Academy?
Да. Каждый урок React Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Фаза отображения и фиксация DOM
- Когда срабатывает useLayoutEffect по сравнению с useEffect
- Измерение элементов DOM с помощью useLayoutEffect
- Как избежать перерасчёта компоновки и визуального мерцания