Кто хоть раз писал useMemo, задавался вопросом: «А оно вообще помогает или просто усложняет мне жизнь?».
И правда в том, что мемоизация — это инструмент, а не обязательный ритуал.
Если использовать её не по делу, можно получить хуже производительность, чем без неё.
Разбираем по шагам.
✅ Когда реально стоит использовать useMemo, useCallback, memo?
1. Тяжёлые вычисления.
const expensiveResult = useMemo(() => calculateHeavyStuff(data), [data]);
Пример: сортировка, фильтрация, агрегация, построение дерева.
Без useMemo эта функция будет срабатывать на каждый ререндер, даже если data не менялась, что в теории может привести к проблемам с оптимизацией.
2. Передача одной и той же функции в props.
const handleClick = useCallback(() => doSomething(id), [id]);
<MyButton onClick={handleClick} />
Если MyButton — memo-компонент, он будет лишний раз ререндериться, если ты передаёшь новую ссылку на функцию каждый раз, а useCallback сохраняет ссылку между рендерами.
3. Стабилизация объектов или массивов в пропсах.
const filters = useMemo(() => ({ search, sort }), [search, sort]);
<List filters={filters} />
Без useMemo объект filters будет создаваться каждый раз заново.
Даже если значения внутри не меняются — List будет думать, что пропсы изменились.
4. Часто ререндерящиеся элементы списков.
const ListItem = memo(({ item }) => { ... });
Когда рендерится большой список (например, 100+ элементов), и каждый элемент получает свои props — memo может сильно сократить количество перерисовок.
❌ Когда мемоизация вредит или не нужна?
- Компонент рендерится редко.
Если компонент обновляется 1 раз в 5 секунд — зачем оптимизировать?
Ты тратишь память и усложняешь код без выигрыша.
- Стабилизируешь значения, которые всё равно пересоздаются.
const filters = useMemo(() => ({ search }), [search]);
Но search приходит с родителя, который всегда создаёт новый prop.
Или filters внутри содержит
Date.now(), Math.random() — memo бесполезен.- Мемоизация маскирует архитектурную ошибку.
Если у тебя компоненты всегда перерисовываются — скорее всего, нужно пересмотреть структуру, а не всё засовывать в memo. Часто на моей практике 90% проблем с оптимизацией решались и решаются правильной декомпозицией.
Например:
<Parent>
<Child data={data} />
</Parent>
Если Parent обновляется часто, Child будет ререндериться, даже если data не изменилась. Лучше вынести Child наружу или использовать React.memo.
- Ты не профилировал, просто на всякий случай воткнул useMemo и это очень частая ошибка.
useMemo(() => 2 + 2, []) // мы все так делали (на этапе «хочу быть крутым»).
Но мемоизация — это накладная операция, она тратит память, удерживает ссылки и мешает сборщику мусора.
🔍 Подводные камни мемоизации.
- Старые значения в замыканиях.
useCallback может замкнуть устаревший стейт, если не указаны зависимости.
- Утечки памяти.
Мемо сохраняет ссылки на объекты и если ты неаккуратно обращаешься с зависимостями, то можешь держать в памяти огромные данные.
- Сложность отладки.
Начинаются баги в стиле «Почему функция не обновляется?», «Почему данные не приходят?». Часто это забытые зависимости или замыкания.
🧠 Что делать правильно?
- Профилируй сначала.
Открой React DevTools, Profiler и посмотри, что реально ререндерится.
- Используй мемоизацию точечно.
Только там, где есть перерисовки, есть вычисления, есть реальные проблемы.
- Проверяй зависимости.
Ошибки в списке зависимостей одна из самых частых причин багов и багрепортов.
- Читай код, а не копируй.
Мемо — это не «умная обёртка», а механизм, который ты должен понимать.
📌 Мемоизация в React как кофе, немного — бодрит, а когда слишком много — появляется тревожность, бессонница и баги.
Оптимизируй там, где надо, а не там, где можно.
📱 Telegram | 📱 Instagram | 📱 Boosty
🟡 Поддержать автора | 🟡 Goal
