Лишние ре-рендеры и как их найти (Profiler)
Урок про то, как найти лишние ре-рендеры с помощью React DevTools Profiler — прежде чем что-либо оптимизировать.
Лишний ре-рендер — это рендер компонента, который не привёл ни к каким изменениям видимого результата. Сам по себе он не «ошибка», но если повторяется часто и в тяжёлом компоненте — это цель для оптимизации.
Сначала измерь, потом чини
Главная ошибка новичка в производительности — раскидать React.memo и useMemo «на всякий случай». Это добавляет сложность и сравнения, которые сами стоят времени. Правильный порядок: замерить → найти узкое место → оптимизировать именно его → перезамерить. Инструмент для замера — React DevTools Profiler.
React DevTools Profiler — обзор
Установите расширение React Developer Tools для браузера. Во вкладке Profiler:
- Нажмите «запись», выполните действие в приложении (клик, ввод текста), остановите запись.
- Profiler покажет flame graph (пламенный граф) каждого коммита: какие компоненты рендерились и сколько это заняло.
- Серые компоненты не рендерились в этом коммите; цветные — рендерились, цвет и ширина отражают время.
- Включите «Record why each component rendered» — DevTools подпишет причину: изменился проп, состояние, контекст или родитель.
Что искать
| Симптом в Profiler | Вероятная причина |
| Большое поддерево рендерится на каждый ввод символа | Состояние формы поднято слишком высоко; стоит локализовать |
| Тяжёлый список перерисовывается, хотя данные те же | Родитель передаёт новый объект/функцию пропсом каждый рендер |
| «Why rendered: parent» у дорогого листа | Кандидат на React.memo |
| Один и тот же компонент в коммите 50 раз | Проблема с ключами в списке |
Дешёвая «ручная» диагностика
Без Profiler можно временно вставить console.log прямо в тело компонента — он печатается на каждый рендер. Это грубо, но мгновенно отвечает на вопрос «сколько раз рендерится этот компонент при этом действии».
function ExpensiveList({ items }) {
console.log("ExpensiveList рендер, items.length =", items.length);
return (
<ul>
{items.map((it) => (
<li key={it.id}>{it.name}</li>
))}
</ul>
);
}
Если этот лог сыплется при действиях, которые items не меняют, — у вас лишние ре-рендеры, и теперь понятно, куда смотреть.
Когда лишний ре-рендер можно игнорировать
Если компонент лёгкий (несколько узлов DOM, без тяжёлых вычислений), пара лишних рендеров незаметна — оптимизация только навредит читаемости. Реагируйте на ре-рендеры, которые видны в Profiler как заметное время или ощущаются как лаги интерфейса.
Итог
- Profiler показывает, какие компоненты рендерились, сколько это заняло и почему.
- Включайте «why did this render», чтобы видеть причину: проп, состояние, контекст, родитель.
- Оптимизируйте только то, что реально дорого и часто. Остальное — оставьте простым.