Производительность в React
Почему компонент перерисовывается, memo, useMemo, useCallback, useTransition, виртуализация.
Обновлено
Что значит производительность в React
React-приложение обычно тормозит по одной из двух причин: оно слишком часто перерисовывается (пересчитываются компоненты, которые не изменились) или делает слишком много работы за один рендер (список из 5000 строк, тяжёлое вычисление). Перед любой оптимизацией — измеряйте: React DevTools → Profiler.
Когда компонент перерисовывается
- Изменилось его состояние.
- Перерисовался родитель (по умолчанию перерисовываются все дети — даже если props одинаковые).
- Изменилось значение Context, который он читает.
Перерисовка не означает изменения DOM — React сравнивает результат и трогает DOM только там, где есть разница. Обычно перерисовки дешёвые; проблемой они становятся у больших или очень частых компонентов.
Инструменты — что делает каждый
| Инструмент | Что делает | Когда |
|---|---|---|
| перенос состояния ниже | перерисовывается только маленький компонент | первый вариант, бесплатно |
children как prop |
переданное содержимое не перерисовывается вместе с родителем | обёртки с состоянием (модалка, аккордеон) |
memo(Component) |
пропускает перерисовку, если props равны (Object.is) |
дорогой компонент, часто перерисовываемый с теми же props |
useMemo(() => calc, deps) |
сохраняет результат вычисления | тяжёлое вычисление или объект, передаваемый в memo |
useCallback(fn, deps) |
сохраняет ту же функцию между рендерами | функция, передаваемая в memo или используемая в deps |
useTransition / useDeferredValue |
помечает обновление как несрочное | фильтрация большого списка при вводе — поле остаётся плавным |
| виртуализация | рендерит только видимые строки | списки из тысяч элементов (TanStack Virtual) |
React Compiler
React Compiler анализирует код при сборке и автоматически добавляет мемоизацию (аналог memo / useMemo / useCallback) там, где нужно. С ним вы пишете простой код, а большая часть ручной мемоизации становится лишней. Next.js поддерживает его через опцию reactCompiler в next.config.ts.
Условие: код соблюдает правила React — чистые компоненты, без мутации props и состояния.
Ловушка ссылок
const Row = memo(function Row({ item, onSelect }) { ... })
function List({ items }) {
return items.map(item => (
<Row key={item.id} item={item} onSelect={() => select(item.id)} /> // НОВАЯ функция на каждом рендере
))
}memo сравнивает props по ссылке; функция или объект, созданные прямо в JSX, всегда «новые» → memo не помогает. Отсюда useCallback / useMemo — или React Compiler.
Порядок оптимизации
- Измерьте (Profiler): какой компонент, сколько длится, почему перерисовывается.
- Перенесите состояние ближе к месту использования; разделите компоненты.
- Server Components для всего неинтерактивного — нулевая стоимость на клиенте.
- Виртуализация для длинных списков;
useTransitionдля тяжёлых обновлений. - Точечные
memo/useMemo/useCallback— или включите React Compiler.
Коротко
- Перерисовка = своё состояние, перерисованный родитель или изменённый Context; измеряйте в Profiler.
- Сначала структура (состояние ниже,
children), потомmemo+useMemo/useCallback. useTransitionдля тяжёлых обновлений, виртуализация для больших списков, React Compiler для автоматической мемоизации.