Как ускорить React-приложение без переписывания всего кода
Большинство React-приложений тормозят не потому что React медленный — а потому что компоненты перерендериваются тогда, когда не должны, или загружают слишком много кода сразу. Хорошая новость: чаще всего можно ускориться без большого рефакторинга.
Сначала измерьте, потом оптимизируйте
Первая ошибка — начинать оптимизацию без данных. Прежде чем что-то менять, откройте React DevTools Profiler и посмотрите, какие компоненты рендерятся чаще всего и сколько времени занимает каждый рендер.
Запишите флейм-граф для типичного сценария: клик по кнопке, ввод в поле, прокрутка списка. Часто выясняется, что 80% времени уходит на один-два компонента, которые вы и не подозревали в проблемах.
React.memo и когда он не помогает
React.memo предотвращает рендер компонента, если его пропсы не изменились. Звучит просто, но есть нюанс: если родительский компонент каждый раз создаёт новые объекты или функции в JSX — memo не сработает, потому что сравнение пропсов по ссылке даст false.
Чтобы memo работал, нужно стабилизировать пропсы. Функции оборачивайте в useCallback, объекты — в useMemo. При этом сам useMemo тоже не бесплатный: добавляйте его только там, где вычисление действительно дорогое.
- memo эффективен для листовых компонентов с примитивными пропсами
- useCallback нужен, когда функция передаётся в memo-компонент
- useMemo оправдан для фильтрации/сортировки больших массивов
- Не оборачивайте всё подряд — overhead есть всегда
Code splitting: загружайте только то, что нужно сейчас
Если весь JavaScript загружается при первом открытии страницы — это почти всегда слишком много. React.lazy и Suspense позволяют загружать компоненты по требованию: модальные окна, вкладки, тяжёлые формы — всё, что пользователь видит не сразу.
В Next.js это ещё проще: dynamic импорт с { ssr: false } автоматически выносит компонент в отдельный чанк. Результат — меньше кода при первой загрузке, быстрее Time to Interactive.
Виртуализация: когда список длинный
Если вы рендерите больше 100-200 элементов за раз — скорее всего, стоит использовать виртуализацию. Библиотеки вроде @tanstack/virtual или react-window рендерят только видимые строки, а не весь список.
Прирост может быть колоссальным: список из 1000 строк без виртуализации рендерится за секунды, с виртуализацией — за миллисекунды. Особенно заметно на мобильных устройствах.
Что не стоит трогать
Не каждая оптимизация стоит своего времени. Если компонент рендерится 2 мс — это нормально. Если список из 20 элементов — виртуализация не нужна. Преждевременная оптимизация добавляет сложности без реальной пользы.
Хорошее правило: если пользователь не жалуется на тормоза и профайлер не показывает проблемных мест — значит, всё работает достаточно хорошо.