Найти в Дзене

Как правильно использовать useMemo и useCallback — без переоптимизации

В мире React-разработки useMemo и useCallback часто воспринимаются как «волшебные таблетки» от всех проблем с производительностью. Их активно вставляют в код, чтобы «улучшить оптимизацию», даже когда в этом нет никакой необходимости. В итоге — раздутый, менее читаемый код, ложное чувство контроля и ни одного реального профита для пользователя. В 2026 году, когда React стал ещё умнее (благодаря Server Components, Actions и streaming SSR), понимание когда и зачем использовать useMemo и useCallback стало критически важным. Не для скорости рендера, а для здравого смысла. Давайте разберёмся, как применять эти хуки правильно, без преждевременной оптимизации. Начнём с основ. Оба хука решают одну и ту же проблему: избежать создания новых объектов или функций на каждом рендере, если это может вызвать ненужные ререндеры у дочерних компонентов (особенно обёрнутых в React.memo). Но, создание новой функции или объекта — это не всегда плохо. На самом деле, в 95% случаев это абсолютно бесплатно для
Оглавление

В мире 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 должен хранить значения, сравнивать зависимости, управлять памятью. Если вы мемоизируете «на всякий случай», вы замедляете приложение, а не ускоряете.

Главная ошибка: «мемоизирую всё подряд»

Представьте такой компонент:

-2

Выглядит «оптимизированно», правда? Но на самом деле — лишняя сложность.

  • Вычисление fullName — это конкатенация двух строк. Это занимает менее микросекунды. useMemo здесь не нужен.
  • Функция handleClick не передаётся в «тяжёлые» дочерние компоненты. Даже если она будет новой при каждом рендере — ничего не сломается и не замедлится.

Это классический пример преждевременной оптимизации — вы платите за сложность, но не получаете выгоды.

✅ Когда useMemo действительно нужен?

1. Дорогие вычисления

Если у вас есть функция, которая замедляет рендер — например, сортировка, фильтрация большого массива, парсинг, рекурсивные вычисления:

-3

Здесь useMemo оправдан: без него при каждом рендере будет запускаться тяжёлая операция, даже если data не изменились.

💡 Как понять, «дорогая» ли операция? Используйте React DevTools Profiler или console.time(). Не гадайте — измеряйте!

2. Стабильные объекты для React.memo

Если вы передаёте объект в компонент, обёрнутый в React.memo, и не хотите, чтобы он ререндерился из-за нового ссылочного значения:

-4

Без useMemo каждый рендер родителя будет создавать новый объект, и ThemeContext.Provider (или дочерний компонент) будет обновляться, даже если данные те же.

✅ Когда useCallback действительно нужен?

1. Функции, передаваемые в мемоизированные дочерние компоненты

-5

Без useCallback, handleAction будет новой функцией при каждом рендере Parent, и Child будет ререндериться, несмотря на React.memo.

🔑 Важно: если Child не обёрнут в React.memo, useCallback не даёт никакой выгоды. React всё равно перерисует его — потому что родитель изменился.

2. Функции, используемые в зависимостях эффектов

Иногда вы передаёте функцию в useEffect как зависимость:

-6

Если onSuccess — обычная функция внутри компонента, она будет меняться при каждом рендере, и эффект будет срабатывать слишком часто. useCallback стабилизирует её:

-7

Но даже здесь — спросите себя: а нельзя ли перенести логику внутрь эффекта? Часто это проще и чище.

❌ Когда НЕ нужно использовать useMemo и useCallback

  • Для простых значений: строки, числа, булевы — они примитивны, их не нужно мемоизировать.
  • Если компонент не использует React.memo: тогда новые функции/объекты не вызовут лишних ререндеров.
  • Если зависимости часто меняются: мемоизация бесполезна, если массив зависимостей почти всегда новый.
  • «На всякий случай»: это анти-паттерн. Оптимизация без профилирования — это просто шум.

🧠 Правило большого пальца (2026)

Не используйте useMemo или useCallback, пока вы не измерили проблему и не убедились, что мемоизация её решает.

React — очень быстр сам по себе. Современные браузеры — ещё быстрее. Большинство «проблем с производительностью» на самом деле вызваны:

  • избыточным рендером из-за плохой архитектуры,
  • отсутствием пагинации/виртуализации,
  • медленными API или сторонними скриптами.

А не тем, что вы не поставили useCallback на обработчик кнопки.

💡 Альтернативы мемоизации

Иногда вместо useMemo/useCallback лучше пересмотреть архитектуру:

  1. Выносите логику в хуки — тогда компоненты становятся «тоньше».
  2. Используйте контексты с осторожностью — часто они вызывают массовые ререндеры. Разбивайте на узкоспециализированные контексты.
  3. Применяйте code splitting и lazy loading — это даст куда больший прирост, чем микромемоизация.
  4. Перейдите на React Server Components — если логика не нужна на клиенте, не отправляйте её туда вообще.

📊 Краткое руководство: стоит ли мемоизировать?

Простое вычисление (a + b) ❌ Нет

Сортировка массива из 10 000 элементов ✅ Да

Функция передаётся в компонент без React.memo❌ Нет

Функция передаётся в React.memo-компонент✅ Да

Объект передаётся в контекст⚠️ Только если контекст вызывает ререндеры

«Хочу, чтобы было быстрее» без профилирования❌❌❌ Нет

Меньше — значит больше

В 2026 году зрелые React-разработчики редко используют useMemo и useCallback. Они понимают: эти хуки — инструменты для узких случаев, а не повседневная практика.

Настоящая оптимизация начинается не с мемоизации, а с:

  • чистой архитектуры,
  • правильного дерева компонентов,
  • минимизации передаваемых props,
  • и, конечно, измерений.

Используйте useMemo и useCallback тогда, когда они решают реальную проблему — а не потому, что «так написано в туториале».

Помните: лишний код — это технический долг. Даже если он «оптимизирующий».