Как оптимизировать React приложение, если страница подвисает при действиях пользователя?
Рендеринг и производительность
«Подвисает при действиях пользователя» — значит главный поток занят: ввод, клики и анимации ждут, пока React (или ваш код) закончит работу. Лечение — по причине.
Сначала измерить
React DevTools Profiler (production-сборка): что рендерится на каждое действие и сколько стоит. Performance-запись в Chrome: длинные задачи (>50 мс), что именно в них — рендер, вычисления, layout. Метрика INP показывает реальную отзывчивость.
Причина 1: слишком много рендеров
Состояние ввода лежит слишком высоко — на каждый символ рендерится вся страница.
- Спустить state вниз, к полю.
- children-как-проп, чтобы обёртка со state не перерисовывала детей.
- React.memo на тяжёлых соседях + стабильные props (useCallback/useMemo).
- Разделить контексты, сузить селекторы стора.
Причина 2: рендер дорогой
- useMemo на тяжёлые вычисления (фильтрация/сортировка тысяч строк).
- Виртуализация больших списков и таблиц (react-window/virtuoso), если в DOM тысячи узлов, мемоизация не спасёт.
Причина 3: срочное соревнуется с несрочным
Ввод блокируется тяжёлой перерисовкой результатов:
const [isPending, startTransition] = useTransition();
const onChange = (e) => {
setQuery(e.target.value); // срочно: поле
startTransition(() => setFilter(e.target.value)); // несрочно: список
};Или useDeferredValue(query) для тяжёлого поддерева. Плюс дебаунс дорогих реакций.
Причина 4: тяжёлые вычисления вне рендера
Парсинг, шифрование, обработка изображений — вынести в Web Worker; длинные циклы разбивать.
Причина 5: layout thrashing
Чередование чтения (getBoundingClientRect) и записи стилей в цикле; тяжёлый useLayoutEffect блокирует кадр.
Коротко для собеседования
Профилировать (Profiler + Performance + INP), затем: убрать лишние рендеры (state вниз, memo, стабильные props, селекторы), удешевить рендер (useMemo, виртуализация), развести приоритеты (useTransition/useDeferredValue, дебаунс), тяжёлые вычисления — в Web Worker, не допускать layout thrashing.