В мире React-разработки useMemo и useCallback часто воспринимаются как «волшебные таблетки» от всех проблем с производительностью. Их активно вставляют в код, чтобы «улучшить оптимизацию», даже когда в этом нет никакой необходимости. В итоге — раздутый, менее читаемый код, ложное чувство контроля и ни одного реального профита для пользователя.
В 2026 году, когда React стал ещё умнее (благодаря Server Components, Actions и streaming SSR), понимание когда и зачем использовать useMemo и useCallback стало критически важным. Не для скорости рендера, а для здравого смысла.
Давайте разберёмся, как применять эти хуки правильно, без преждевременной оптимизации.
🔍 Что делают useMemo и useCallback на самом деле?
Начнём с основ.
- useMemo(fn, deps) — запоминает результат выполнения функции между рендерами, если зависимости не изменились.
- useCallback(fn, deps) — запоминает саму функцию, чтобы её ссылка не менялась при каждом рендере.
Оба хука решают одну и ту же проблему: избежать создания новых объектов или функций на каждом рендере, если это может вызвать ненужные ререндеры у дочерних компонентов (особенно обёрнутых в React.memo).
Но, создание новой функции или объекта — это не всегда плохо. На самом деле, в 95% случаев это абсолютно бесплатно для производительности.
Почему? Потому что JavaScript очень быстр в создании простых объектов и функций. А вот мемоизация — это дополнительная работа: React должен хранить значения, сравнивать зависимости, управлять памятью. Если вы мемоизируете «на всякий случай», вы замедляете приложение, а не ускоряете.
Главная ошибка: «мемоизирую всё подряд»
Представьте такой компонент:
Выглядит «оптимизированно», правда? Но на самом деле — лишняя сложность.
- Вычисление fullName — это конкатенация двух строк. Это занимает менее микросекунды. useMemo здесь не нужен.
- Функция handleClick не передаётся в «тяжёлые» дочерние компоненты. Даже если она будет новой при каждом рендере — ничего не сломается и не замедлится.
Это классический пример преждевременной оптимизации — вы платите за сложность, но не получаете выгоды.
✅ Когда useMemo действительно нужен?
1. Дорогие вычисления
Если у вас есть функция, которая замедляет рендер — например, сортировка, фильтрация большого массива, парсинг, рекурсивные вычисления:
Здесь useMemo оправдан: без него при каждом рендере будет запускаться тяжёлая операция, даже если data не изменились.
💡 Как понять, «дорогая» ли операция? Используйте React DevTools Profiler или console.time(). Не гадайте — измеряйте!
2. Стабильные объекты для React.memo
Если вы передаёте объект в компонент, обёрнутый в React.memo, и не хотите, чтобы он ререндерился из-за нового ссылочного значения:
Без useMemo каждый рендер родителя будет создавать новый объект, и ThemeContext.Provider (или дочерний компонент) будет обновляться, даже если данные те же.
✅ Когда useCallback действительно нужен?
1. Функции, передаваемые в мемоизированные дочерние компоненты
Без useCallback, handleAction будет новой функцией при каждом рендере Parent, и Child будет ререндериться, несмотря на React.memo.
🔑 Важно: если Child не обёрнут в React.memo, useCallback не даёт никакой выгоды. React всё равно перерисует его — потому что родитель изменился.
2. Функции, используемые в зависимостях эффектов
Иногда вы передаёте функцию в useEffect как зависимость:
Если onSuccess — обычная функция внутри компонента, она будет меняться при каждом рендере, и эффект будет срабатывать слишком часто. useCallback стабилизирует её:
Но даже здесь — спросите себя: а нельзя ли перенести логику внутрь эффекта? Часто это проще и чище.
❌ Когда НЕ нужно использовать useMemo и useCallback
- Для простых значений: строки, числа, булевы — они примитивны, их не нужно мемоизировать.
- Если компонент не использует React.memo: тогда новые функции/объекты не вызовут лишних ререндеров.
- Если зависимости часто меняются: мемоизация бесполезна, если массив зависимостей почти всегда новый.
- «На всякий случай»: это анти-паттерн. Оптимизация без профилирования — это просто шум.
🧠 Правило большого пальца (2026)
Не используйте useMemo или useCallback, пока вы не измерили проблему и не убедились, что мемоизация её решает.
React — очень быстр сам по себе. Современные браузеры — ещё быстрее. Большинство «проблем с производительностью» на самом деле вызваны:
- избыточным рендером из-за плохой архитектуры,
- отсутствием пагинации/виртуализации,
- медленными API или сторонними скриптами.
А не тем, что вы не поставили useCallback на обработчик кнопки.
💡 Альтернативы мемоизации
Иногда вместо useMemo/useCallback лучше пересмотреть архитектуру:
- Выносите логику в хуки — тогда компоненты становятся «тоньше».
- Используйте контексты с осторожностью — часто они вызывают массовые ререндеры. Разбивайте на узкоспециализированные контексты.
- Применяйте code splitting и lazy loading — это даст куда больший прирост, чем микромемоизация.
- Перейдите на React Server Components — если логика не нужна на клиенте, не отправляйте её туда вообще.
📊 Краткое руководство: стоит ли мемоизировать?
Простое вычисление (a + b) ❌ Нет
Сортировка массива из 10 000 элементов ✅ Да
Функция передаётся в компонент без React.memo❌ Нет
Функция передаётся в React.memo-компонент✅ Да
Объект передаётся в контекст⚠️ Только если контекст вызывает ререндеры
«Хочу, чтобы было быстрее» без профилирования❌❌❌ Нет
Меньше — значит больше
В 2026 году зрелые React-разработчики редко используют useMemo и useCallback. Они понимают: эти хуки — инструменты для узких случаев, а не повседневная практика.
Настоящая оптимизация начинается не с мемоизации, а с:
- чистой архитектуры,
- правильного дерева компонентов,
- минимизации передаваемых props,
- и, конечно, измерений.
Используйте useMemo и useCallback тогда, когда они решают реальную проблему — а не потому, что «так написано в туториале».
Помните: лишний код — это технический долг. Даже если он «оптимизирующий».