Как происходит перерисовка в React?
Рендеринг и производительность
Перерисовка в React — конвейер из трёх стадий: триггер → рендер (вычисления) → коммит (правки DOM). «Перерисовка» начинается с вычисления, а DOM меняется только в конце.
Триггер
Сеттер state / dispatch, изменение контекста, обновление подписанного стора — компонент помечается «грязным», обновление ставится в очередь планировщика (несколько сеттеров батчатся в одно обновление).
Render-фаза
- React вызывает функцию помеченного компонента и рекурсивно — его детей.
- Каждый вызов возвращает новое поддерево React-элементов (Virtual DOM).
- Reconciliation: новое дерево сравнивается со старым — по типам узлов и key. Одинаковый тип → узел переиспользуется, сравниваются props и дети; разный → поддерево пересоздаётся.
- Фаза чистая и (с React 18) прерываемая: срочное обновление (ввод) может «влезть» посреди несрочного.
Commit-фаза
- Собранный список изменений применяется к реальному DOM одним синхронным куском.
- Обновляются refs, синхронно выполняются useLayoutEffect.
- Браузер отрисовывает кадр; после — асинхронно useEffect.
Следствия
const List = () => {
console.log('render'); // вызов ≠ изменение DOM
return <ul>...</ul>;
};- Компонент может отрендериться, а DOM не измениться (дифф не нашёл отличий) — рендер дешевле, чем кажется, но не бесплатен.
- Родитель рендерит детей по умолчанию всегда; пропуски — только через React.memo/стабильные элементы.
- DOM-состояние (фокус, ввод) сохраняется, потому что узлы переиспользуются, а не пересоздаются.
Коротко для собеседования
Перерисовка: триггер (сеттер/контекст/стор) → render-фаза (вызовы компонентов, построение нового Virtual DOM, дифф по типам и key; чистая и прерываемая) → commit (минимальные правки DOM, layout-эффекты) → отрисовка → useEffect. Рендер — вычисление; DOM меняется только на величину диффа.