Я долго считал, что проблемы с производительностью в React — это всегда про медленные API, тяжелые алгоритмы или кривые запросы к базе. Ререндеры? Да кому они мешают, Virtual DOM же! Но когда в одном из проектов экран начал тормозить на 800ms при клике на кнопку, пришлось разбираться.
🔍 Проблема: латентные ререндеры
Первое, что сбивает с толку — приложение не падает, а просто “подтупливает”. Нет явных ошибок, но интерфейс перестает быть отзывчивым. В моем случае:
- Сетевые запросы укладывались в 50-100ms
- Бэкенд отдавал данные за 2-3 SQL-запроса
- Но после получения данных интерфейс все равно “задумывался”
Профилирование через React DevTools показало: проблема не в одном тяжелом компоненте, а в сотнях мелких ререндеров по всему приложению. Компоненты, которые вообще не зависели от изменившихся данных, все равно выполняли свою render-логику.
📊 Почему это дорого
Даже если DOM не обновляется, каждый ререндер требует:
- Вызова функционального компонента
- Пересчета всех хуков внутри
- Генерации нового JSX
- Сравнения в Virtual DOM
На примере нашего проекта:
// Было: состояние в корне
const App = () => {
const [user, setUser] = useState(null); // Обновляется редко
// ...100+ дочерних компонентов
}
// Стало: поднятие контента + опускание состояния
const UserProfile = () => {
const [user, setUser] = useState(null);
return <ProfileContent user={user} />;
}
После рефакторинга количество ненужных ререндеров упало на 70% только за счет правильного размещения состояния.
🛠️ Три паттерна для исправления
-
Локализация состояния
ПереносuseStateближе к месту использования сокращает “зону поражения” при обновлении. Если компонент<Header>не используетuserData, зачем ему ререндериться при его изменении? -
Стабильные ссылки
Функции и объекты в пропсах — главные враги:// Плохо: новая функция на каждый рендер <Button onClick={() => submit(data)} /> // Лучше: useCallback или вынос за пределы компонента const handleSubmit = useCallback(() => submit(data), [data]); <Button onClick={handleSubmit} /> -
Композиция вместо монолитов
Разбиваем “боговые” компоненты на:- Контейнеры (логика + состояние)
- Presentational (чистый UI)
- Утилиты (хуки, HOC)
⚡ Что дало результат
После рефакторинга:
- Время отклика упало с 800ms до 60ms
- Количество ререндеров на действие сократилось в 5-10 раз
- Профиль рендеров стал предсказуемым
Главный инсайт: проблемы с производительностью в React редко решаются точечными оптимизациями. Чаще всего это следствие неправильной компоновки компонентов и состояния. Прежде чем хвататься за useMemo, стоит проверить — а тот ли компонент вообще должен ререндериться?
Для глубокого анализа рекомендую:
- React DevTools Profiler
why-did-you-render- Ручной замер через
performance.now()
Как показывает практика, 80% проблем решается не memo-хуками, а нормальной компонентной архитектурой. Остальные 20% — это уже edge cases, где действительно нужны тонкие оптимизации.
Источник: https://dev.to/akshay_sarak/the-real-cost-of-re-renders-in-react-2j77