0Pricing
React Academy · Урок

Как избежать перерасчёта компоновки и визуального мерцания

Определяйте, когда 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 — локальная установка не требуется.

Все уроки этого курса

  1. Фаза отображения и фиксация DOM
  2. Когда срабатывает useLayoutEffect по сравнению с useEffect
  3. Измерение элементов DOM с помощью useLayoutEffect
  4. Как избежать перерасчёта компоновки и визуального мерцания
← Назад к React Academy