React

Что такое render и когда он происходит

Новичок открывает консоль и видит, что компонент логирует себя дважды на один клик. Паника: бесконечный цикл? Утечка? На самом деле это две разные вещи, которые легко спутать. Первая - StrictMode намеренно вызывает компонент дважды при разработке, чтобы поймать нечистый код. Вторая - непонимание, что вообще такое рендер: это не рисование на экране, а всего лишь вызов функции компонента. React сначала вычисляет, как должен выглядеть UI, и только потом точечно меняет DOM. Разделение этих двух фаз снимает большинство загадок про то, почему интерфейс ведёт себя именно так.

  • React DevTools умеет подсвечивать ре-рендеры компонентов - незаменимый инструмент для поиска лишних перерисовок
  • Performance Tracks в React 19.2 показывают фазы рендера и коммита прямо в профайлере Chrome
  • React Compiler v1.0 автоматически убирает лишние ре-рендеры, но понимать, что их вызывает, всё равно необходимо
  • StrictMode включён по умолчанию в новых проектах на Next.js и Vite - двойной вызов в разработке встречают все

Предварительные знания

  • useState: вызов сеттера планирует обновление компонента
  • Компоненты - чистые функции от props и состояния
  • Reconciliation из вводного урока: React сравнивает старое и новое описание UI

Фаза рендера против фазы коммита

Слово 'рендер' сбивает с толку: кажется, что речь про рисование на экране. На деле рендер в React - это просто вызов функции компонента. Компонент возвращает описание UI (дерево элементов), React сравнивает его с предыдущим (reconciliation) и вычисляет, что изменилось. Это фаза рендера, и она ничего не трогает в DOM. Только после неё наступает фаза коммита, когда React вносит вычисленные правки в реальный DOM.

  1. Триггер: появился повод обновиться - первый рендер или изменение состояния
  2. Рендер: React вызывает компоненты, получает новое описание UI и сравнивает со старым (reconciliation)
  3. Коммит: React применяет к реальному DOM только вычисленную разницу

Разделение фаз - ключ к производительности React. Вызвать функцию и сравнить объекты в памяти дёшево. Трогать реальный DOM дорого. Поэтому React сначала всё просчитывает в фазе рендера и в коммите вносит минимум изменений. Если описание UI не изменилось, фаза коммита для этого узла может вообще не понадобиться.

Именно потому, что рендер - это вызов функции, частый ре-рендер сам по себе не катастрофа: пересчёт в памяти быстр. Проблемой он становится, только когда внутри компонента идут тяжёлые вычисления. С этим борются мемоизацией, а в React 19 - автоматически через компилятор.

Что именно происходит в фазе рендера?

Что запускает ре-рендер

Поводов для рендера всего несколько, и важно знать их точно. Первый - монтирование: компонент рендерится впервые при появлении на экране. Дальше повторный рендер запускает только изменение состояния через сеттер. И есть каскадный эффект: когда компонент ре-рендерится, по умолчанию вместе с ним заново вызываются все его дочерние компоненты.

ПоводЗапускает ре-рендер
Первый рендер (монтирование)Да, начальный
Вызов сеттера состоянияДа, если значение реально изменилось
Ре-рендер родителяДа, дети перерисовываются по умолчанию
Мутация обычной переменнойНет, React об этом не знает

Важная деталь: изменение props само по себе не является отдельным триггером. Props меняются потому, что ре-рендерится родитель, который их передаёт. То есть корень любого ре-рендера - всегда либо монтирование, либо чей-то setState выше или внутри компонента. Обычная переменная, изменённая в обход состояния, перерисовку не вызовет вовсе.

Если сеттеру передать значение, равное текущему, React может пропустить ре-рендер - это встроенная оптимизация (bail out). Для объектов сравнение идёт по ссылке, поэтому новый объект с теми же полями всё равно считается изменением.

Что НЕ вызывает ре-рендер компонента?

Чистый рендер и StrictMode

Фаза рендера обязана быть чистой. Это значит: при одних и тех же props и состоянии компонент возвращает один и тот же JSX и не делает побочных эффектов во время рендера. Нельзя менять переменные вне компонента, мутировать props или состояние, обращаться к сети прямо в теле функции. Побочные эффекты выносят в обработчики событий или в useEffect, которые срабатывают уже после рендера.

Чтобы помочь поймать нечистый рендер, в режиме разработки работает StrictMode. Он намеренно вызывает функции компонентов дважды. Если рендер чистый, двойной вызов незаметен: результат тот же. Если же компонент мутирует что-то снаружи, как Bad выше, удвоение сразу выявит баг - значение подскочит непредсказуемо. В продакшен-сборке двойного вызова нет, накладных расходов это не создаёт.

Двойной лог в консоли при разработке - это не баг и не бесконечный цикл, а намеренное поведение StrictMode. Если же удвоение ломает логику (например, счётчик прибавляет дважды), значит, рендер нечист и его нужно исправить, а не отключать StrictMode.

Зачем StrictMode вызывает компоненты дважды в режиме разработки?

Связь с другими темами

Эта тема замыкает фундамент: становится понятно, как всё изученное складывается в работающий цикл:

  • useState — Вызов сеттера - главный триггер ре-рендера, рассмотренного здесь
  • Списки и keys — key используется именно в фазе reconciliation для сопоставления элементов
  • React Compiler — Авто-мемоизация компилятора борется именно с лишними ре-рендерами из этого урока

Итог

  • Рендер - это вызов функции компонента, который возвращает описание UI. Это не изменение экрана само по себе
  • Цикл состоит из трёх фаз: триггер (повод), рендер (вызов компонентов и reconciliation), коммит (точечные правки DOM)
  • Ре-рендер запускает либо первый рендер, либо изменение состояния через сеттер, либо ре-рендер родителя
  • Рендер должен быть чистым: одни и те же props и состояние дают один и тот же JSX без побочных эффектов
  • В режиме разработки StrictMode вызывает компоненты дважды, чтобы выявить нечистый код. В продакшене этого нет

Связанные уроки

  • rc-06-usestate — Ре-рендер запускается вызовом сеттера состояния, поэтому модель useState нужна прежде понимания рендера
  • rc-04-rendering-lists-keys — Сопоставление элементов по key - часть фазы reconciliation, которую разбирает этот урок
  • rc-20-react-compiler — Понимание лишних ре-рендеров ведёт к React Compiler, который автоматически их убирает
Что такое render и когда он происходит

0

1

Войти