← Назад к вопросам
Как оптимизировать React приложение, если страница подвисает при действиях пользователя?
2.0 Middle🔥 241 комментариев
#React#Архитектура и паттерны
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Оптимизация React приложения
Подвисание страницы при действиях пользователя указывает на производительность проблемы. Существует множество стратегий для диагностики и исправления этого.
1. Профилирование с React DevTools Profiler
Первый шаг - найти узкие места:
// React DevTools Profiler показывает:
// - Время рендеринга каждого компонента
// - Количество рендеров
// - Какой компонент пересчитался без причины
// Используй Profiler API для детального анализа
import { Profiler } from "react";
function onRenderCallback(
id, // компонент
phase, // mount или update
actualDuration, // время рендеринга
baseDuration,
startTime,
commitTime
) {
console.log(`${id} (${phase}) took ${actualDuration}ms`);
}
<Profiler id="MyComponent" onRender={onRenderCallback}>
<MyComponent />
</Profiler>
2. Мемоизация с React.memo
Предотвращай ненужные рендеры дочерних компонентов:
const ExpensiveComponent = React.memo(({ data, onUpdate }) => {
console.log("Component rendered");
return <div>{data}</div>;
});
// Без memo: рендеривается при каждом обновлении родителя
// С memo: рендеривается только если props изменились
// Кастомное сравнение props
const CustomMemo = React.memo(
MyComponent,
(prevProps, nextProps) => {
// true = props равны, пропустить рендер
// false = пересчитать
return prevProps.id === nextProps.id;
}
);
3. useMemo для дорогих вычислений
Кэшируй результаты вычислений:
function MyComponent({ data, filter }) {
// БЕЗ useMemo - пересчитывается каждый рендер
// const filteredData = data.filter(item => item.name.includes(filter));
// С useMemo - пересчитывается только если data или filter изменились
const filteredData = useMemo(
() => data.filter(item => item.name.includes(filter)),
[data, filter]
);
return <ul>{filteredData.map(item => <li key={item.id}>{item.name}</li>)}</ul>;
}
4. useCallback для стабильных функций
Передавай стабильные колбэки дочерним компонентам:
function Parent() {
// БЕЗ useCallback - новая функция каждый рендер
// const handleClick = () => { ... };
// С useCallback - та же функция пока зависимости не изменились
const handleClick = useCallback(() => {
console.log("Clicked");
}, []); // пустые зависимости = функция создаётся один раз
return <Child onClick={handleClick} />; // Child с React.memo не пересчитается
}
5. Код-сплиттинг и ленивая загрузка
Загружай компоненты только когда они нужны:
import { lazy, Suspense } from "react";
const HeavyComponent = lazy(() => import("./HeavyComponent"));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
);
}
6. Виртуализация длинных списков
Рендери только видимые элементы:
import { FixedSizeList } from "react-window";
function VirtualList({ items }) {
return (
<FixedSizeList
height={600}
itemCount={items.length}
itemSize={35}
width="100%"
>
{({ index, style }) => (
<div style={style}>{items[index]}</div>
)}
</FixedSizeList>
);
}
7. Дебаунсинг и троттлинг
Ограничивай частоту обновлений:
import { useCallback, useRef } from "react";
function useDebounce(callback, delay) {
const timeoutRef = useRef(null);
return useCallback((...args) => {
clearTimeout(timeoutRef.current);
timeoutRef.current = setTimeout(() => callback(...args), delay);
}, [callback, delay]);
}
function SearchComponent() {
const [query, setQuery] = useState("");
const debouncedSearch = useDebounce((value) => {
// API запрос только через 300ms после последнего ввода
console.log("Searching:", value);
}, 300);
return (
<input
value={query}
onChange={(e) => {
setQuery(e.target.value);
debouncedSearch(e.target.value);
}}
/>
);
}
8. State структура
Избегай больших state объектов:
// ПЛОХО - весь state обновляется => весь компонент рендеривается
const [state, setState] = useState({
user: {...},
posts: [...],
comments: [...]
});
// ХОРОШО - разделяй логику
const [user, setUser] = useState({...});
const [posts, setPosts] = useState([...]);
const [comments, setComments] = useState([...]);
// ИЛИ используй Context для логического разделения
const UserContext = createContext();
const PostContext = createContext();
9. Chrome DevTools Performance Tab
Используй встроенные инструменты:
- Открой DevTools -> Performance
- Нажми Record
- Выполни действие
- Посмотри где выскочила жёлтая/красная полоса
- Улучшай узкие места
10. Ключевые метрики производительности
// Отслеживай Web Vitals
import { getCLS, getFID, getFCP, getLCP, getTTFB } from "web-vitals";
getLCP(console.log); // Largest Contentful Paint
getFID(console.log); // First Input Delay
getCLS(console.log); // Cumulative Layout Shift
Чеклист оптимизации
- Профилируй с React DevTools
- Обёртывай дорогие компоненты в React.memo
- Кэшируй вычисления useMemo
- Стабилизируй функции useCallback
- Раздели state логически
- Виртуализируй длинные списки
- Используй дебаунсинг для input событий
- Лениво загружай компоненты
- Оптимизируй размер bundle
- Отслеживай Web Vitals