Как работает reconciliation в React?

SeniorReact · Frontend·Обновлено 23 июня 2026
Коротко
Reconciliation — это процесс, при котором React сравнивает виртуальный DOM предыдущего и нового рендера с помощью алгоритма диффинга, чтобы минимально необходимым образом обновить реальный DOM.

Что такое reconciliation

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

Алгоритм диффинга

Наивное сравнение двух деревьев имеет сложность O(n³), что неприемлемо для UI. React использует эвристический алгоритм со сложностью O(n), основанный на двух ключевых предположениях:

  1. Элементы разных типов производят разные деревья — если тип корневого элемента изменился (например, <div><span>), React уничтожает всё старое поддерево и строит новое с нуля.
  2. Ключи (key) помогают идентифицировать стабильные элементы — при рендере списков React использует key для сопоставления элементов между рендерами.

Этапы сравнения

Сравнение по типу

Если тип элемента совпадает — React обновляет только изменившиеся атрибуты/пропсы. Если тип изменился — старый узел уничтожается (componentWillUnmount, очистка эффектов), монтируется новый (componentDidMount, эффекты).

// React переиспользует DOM-узел <input>, обновив только className
// До: <input className="old" />
// После: <input className="new" />

Reconciliation списков

// Без key React может перемонтировать все элементы при добавлении в начало
const BadList = ({ items }: { items: string[] }) => (
  <ul>
    {items.map((item, index) => (
      <li key={index}>{item}</li> // Плохо: индекс как key при изменяемом порядке
    ))}
  </ul>
);

const GoodList = ({ items }: { items: { id: number; name: string }[] }) => (
  <ul>
    {items.map((item) => (
      <li key={item.id}>{item.name}</li> // Хорошо: стабильный уникальный key
    ))}
  </ul>
);

React Fiber

С версии React 16 reconciliation реализован через архитектуру Fiber. Каждый элемент дерева представлен объектом Fiber — связным списком, позволяющим прерывать и возобновлять работу.

Работа делится на две фазы:

  • Render-фаза (reconciliation) — чистая, без побочных эффектов, может быть прервана планировщиком (Scheduler). React строит «work-in-progress» дерево.
  • Commit-фаза — синхронная, применяет изменения к реальному DOM и запускает эффекты (useEffect, useLayoutEffect).
// Пример: почему render может вызываться несколько раз в StrictMode
// React намеренно двойной вызов для выявления побочных эффектов в render-фазе
const Component = () => {
  console.log('render'); // В StrictMode (dev) выведется дважды
  return <div>Hello</div>;
};

Оптимизации

  • React.memo / PureComponent — пропускают reconciliation при неизменных пропсах
  • useMemo / useCallback — стабилизируют ссылки, предотвращая ненужные перерендеры дочерних компонентов
  • key на нестабильных элементах — намеренный сброс состояния компонента

Что хочет услышать интервьюер

Понимание двухфазной модели: render-фаза (построение дерева, диффинг) и commit-фаза (применение к DOM)

Знание эвристик алгоритма O(n): сравнение по типу элемента и роль ключей

Понимание архитектуры Fiber и зачем она нужна (прерываемость, приоритеты задач)

Осознание последствий неправильного использования key (особенно индекс в динамических списках)

Умение связать reconciliation с практическими оптимизациями: memo, useMemo, useCallback

Пример: Влияние типа элемента на reconciliation

// Смена типа — React уничтожит Counter и потеряет его state
const App = ({ isAdmin }: { isAdmin: boolean }) => {
  return isAdmin
    ? <div><Counter /></div>   // Counter монтируется заново при каждом переключении
    : <span><Counter /></span>;
};

// Сохранение state — одинаковый тип на одной позиции
const AppFixed = ({ isAdmin }: { isAdmin: boolean }) => {
  return (
    <div>
      <Counter /> {/* state сохраняется — div не меняется */}
    </div>
  );
};

Пример: Key для принудительного сброса состояния

// Используем key для намеренного сброса state компонента
// без изменения его типа или позиции в дереве
const ProfilePage = ({ userId }: { userId: number }) => {
  return (
    <UserProfile
      key={userId} // При смене userId React уничтожит старый и создаст новый
      userId={userId}
    />
  );
};

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

Путают reconciliation с виртуальным DOM — Virtual DOM это структура данных, reconciliation — алгоритм работы с ней

Считают, что React всегда перерисовывает весь реальный DOM при каждом setState

Используют индекс массива как key в списках с изменяемым порядком элементов, не понимая последствий

Не знают о существовании двух фаз (render/commit) и не понимают, почему побочные эффекты нельзя делать в render-фазе

Путают понятия «перерендер компонента» и «обновление реального DOM» — первое не всегда влечёт второе

Лучшие курсы по теме

изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Feature-Sliced Design

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.5
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее