Зачем нужна мемоизация в React?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Мемоизация в React
Зачем нужна мемоизация?
По умолчанию React перерендеривает компонент и все его дочерние компоненты при каждом изменении state или props родителя. Это нормально для простых приложений, но в сложных UI это может приводить к лишним вычислениям и медленным перерисовкам. Мемоизация — это техника кэширования результатов вычислений или рендеров для пропуска повторной работы, если входные данные не изменились.
Три инструмента мемоизации в React
1. React.memo — мемоизация компонента
Оборачивает компонент и пропускает его повторный рендер, если props не изменились (shallow comparison).
const ExpensiveList = React.memo(function ExpensiveList({ items }) {
console.log("render ExpensiveList");
return (
<ul>
{items.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
);
});
function Parent() {
const [count, setCount] = useState(0);
const items = [{ id: 1, name: "Item" }]; // проблема — новая ссылка каждый раз!
return (
<div>
<button onClick={() => setCount(c => c + 1)}>{count}</button>
<ExpensiveList items={items} />
</div>
);
}
Важно: React.memo делает поверхностное сравнение. Если передавать объекты/функции, создаваемые при каждом рендере, мемоизация не сработает.
2. useMemo — мемоизация вычисленного значения
Кэширует результат дорогостоящего вычисления между рендерами.
function FilteredList({ items, query }) {
// Без useMemo — фильтрация при каждом рендере
const filtered = useMemo(() => {
return items.filter(item =>
item.name.toLowerCase().includes(query.toLowerCase())
);
}, [items, query]); // пересчитывается только если items или query изменились
return <ul>{filtered.map(i => <li key={i.id}>{i.name}</li>)}</ul>;
}
Также useMemo используют для сохранения ссылочной стабильности объектов, передаваемых в React.memo-компоненты.
const options = useMemo(() => ({ theme: "dark", lang: "ru" }), []);
// Теперь options — одна и та же ссылка при каждом рендере
3. useCallback — мемоизация функции
Сохраняет ссылку на функцию между рендерами. Функции пересоздаются при каждом рендере, что ломает React.memo у дочерних компонентов.
function Parent() {
const [count, setCount] = useState(0);
// Без useCallback — новая ссылка при каждом рендере → Child всегда перерендерится
const handleClick = useCallback(() => {
console.log("clicked");
}, []); // зависимостей нет — функция создаётся один раз
return (
<div>
<button onClick={() => setCount(c => c + 1)}>{count}</button>
<Child onClick={handleClick} />
</div>
);
}
const Child = React.memo(({ onClick }) => {
console.log("render Child");
return <button onClick={onClick}>Action</button>;
});
Когда применять мемоизацию?
Стоит использовать:
- Дорогостоящие вычисления (фильтрация/сортировка больших массивов, сложные расчёты)
- Компоненты, которые часто рендерятся без изменения props (таблицы, списки с сотнями строк)
- Стабилизация ссылок для дочерних компонентов с
React.memo - Колбэки, передаваемые в хуки с массивами зависимостей (
useEffect,useMemo)
Не стоит применять везде:
- Мемоизация сама по себе имеет стоимость (сравнение зависимостей + память)
- Простые компоненты дешевле перерендерить, чем сравнивать
- Преждевременная оптимизация ухудшает читаемость кода
React Compiler (React 19)
В React 19 появился React Compiler (ранее React Forget), который автоматически добавляет мемоизацию там, где это нужно. Цель — избавить разработчиков от ручного написания useMemo и useCallback. При его использовании ручная мемоизация становится менее необходимой.
Итог
Мемоизация нужна для предотвращения лишних рендеров и вычислений. Три инструмента решают разные задачи: React.memo — пропуск рендера компонента, useMemo — кэш вычисленного значения, useCallback — кэш ссылки на функцию. Применять следует осознанно — только там, где есть реальный перформанс-профит.