← Назад к списку
ТехническаяFrontendMiddle

Как React решает, что перерисовать? Расскажите про согласование (reconciliation) и роль ключей в списках.

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

  • Рендер — вызов компонентов и построение дерева элементов
  • React сравнивает новое дерево с предыдущим (diffing)
  • В DOM вносятся только найденные различия (commit)
  • Разные типы элементов — поддерево пересоздаётся целиком
  • Ключи сопоставляют элементы списка между рендерами
  • Индекс как ключ ломает состояние при вставках и сортировке
  • По умолчанию рендер родителя рендерит и детей

React строит новое дерево элементов, сравнивает его со старым по эвристикам (тип элемента и ключ) и точечно применяет различия к DOM, поэтому стабильные ключи критичны для списков.

Как сказать вслух

пример ответа

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

Подробный ответ

Основной ответ

Обновление в React состоит из двух фаз. В фазе рендера React вызывает функции компонентов и получает новое дерево элементов — лёгких объектов-описаний. Затем идёт согласование: новое дерево сравнивается с текущим. Полное сравнение деревьев слишком дорого, поэтому используются эвристики: элементы разных типов (div против span, другой компонент) не сравниваются — старое поддерево размонтируется вместе с состоянием, новое монтируется; элементы одного типа сохраняют DOM-узел, обновляются только изменившиеся атрибуты. Дети в списках сопоставляются по ключам: ключ говорит «это тот же элемент, что и раньше», позволяя переставлять узлы вместо пересоздания. В фазе коммита накопленные изменения применяются к DOM. Рендер родителя по умолчанию запускает рендер всех детей; это оптимизируют через React.memo, мемоизацию пропсов и правильную композицию (children).

Ключевые моменты

  • Рендер не равно DOM. Вызов компонента — это дёшево; дорогие операции с DOM происходят только для реальных различий в коммите.
  • Эвристика типов. Смена типа элемента уничтожает поддерево вместе со состоянием — поэтому нельзя объявлять компонент внутри компонента.
  • Ключи в списках. Стабильный ключ из данных (id) сохраняет состояние и DOM элемента при перестановках; индекс безопасен только для статичных списков.
  • Каскад рендеров. Ререндер идёт сверху вниз; memo и вынос состояния ниже по дереву ограничивают его зону.

Практический контекст

Это ядро понимания React: отсюда растут ответы про потерянное состояние инпутов в списках, лишние ререндеры и React.memo. На собеседовании часто дают баг: список с key={index}, при удалении элемента «едут» значения инпутов — нужно объяснить причину. В работе то же знание помогает читать профайлер React DevTools и находить, почему перерисовывается половина приложения.

Частые ошибки

  • Считают, что «Virtual DOM быстрый» сам по себе, не понимая, что он просто экономит обращения к DOM
  • Используют индекс массива как ключ в изменяемых списках
  • Не знают, что смена типа элемента сбрасывает состояние всего поддерева

ИП Кочкин Алексей Сергеевич · ИНН 390509026279 · ОГРНИП 325390000030973 · jiniys2005@yandex.ru