Чем отличается `useMemo` от `useCallback`?

MiddleReact · Frontend·Обновлено 22 июня 2026
Коротко
useMemo мемоизирует результат вычисления (значение), а useCallback мемоизирует саму функцию. По сути useCallback(fn, deps) — это сокращение для useMemo(() => fn, deps).

Основное различие

useMemo и useCallback — оба хука мемоизации в React, но они решают разные задачи.

useMemo принимает фабричную функцию и массив зависимостей, выполняет её и возвращает закешированный результат (любое значение: объект, массив, число, строку). При изменении зависимостей фабрика перезапускается и возвращает новое значение.

useCallback принимает функцию и массив зависимостей и возвращает саму функцию, гарантируя стабильную ссылку между рендерами. При изменении зависимостей создаётся новая функция.

Формула эквивалентности

Под капотом useCallback реализован через useMemo:

// useCallback — это сокращение:
useCallback(fn, deps) === useMemo(() => fn, deps)

Когда использовать useMemo

Применяется для дорогостоящих вычислений, результат которых не должен пересчитываться на каждый рендер: фильтрация/сортировка больших массивов, сложные математические операции, формирование производных данных.

Когда использовать useCallback

Применяется для стабилизации ссылки на функцию, которая:

  • передаётся как пропс в дочерний компонент, обёрнутый React.memo
  • указана в массиве зависимостей useEffect, useMemo или другого useCallback
  • используется как обработчик события в оптимизированном компоненте

Важные нюансы

Мемоизация не бесплатна. Оба хука потребляют память и добавляют накладные расходы на сравнение зависимостей. Преждевременная оптимизация с ними может замедлить код, а не ускорить его.

Референсное равенство. React сравнивает зависимости по Object.is. Примитивы сравниваются по значению, объекты и функции — по ссылке. Именно поэтому функция без useCallback создаётся заново на каждом рендере и сбивает мемоизацию дочерних компонентов.

Правило профилирования. Следует использовать React DevTools Profiler и выявлять реальные узкие места, а не оборачивать всё подряд в мемоизацию.

Сводная таблица

useMemo useCallback
Возвращает Результат вызова функции Саму функцию
Применение Тяжёлые вычисления Стабильные ссылки на функции
Типичный кейс Фильтрация списка Обработчик для React.memo-компонента

Что хочет услышать интервьюер

Чёткое разграничение: useMemo кеширует значение, useCallback кеширует функцию

Понимание того, что useCallback — это частный случай useMemo с возвратом функции

Знание практических сценариев применения каждого хука (React.memo, useEffect-зависимости)

Осознание того, что мемоизация имеет собственную стоимость и не является серебряной пулей

Понимание референсного равенства и того, почему функции без useCallback пересоздаются каждый рендер

Пример: useMemo — мемоизация тяжёлого вычисления

import { useMemo, useState } from 'react';

function ProductList({ products }: { products: Product[] }) {
  const [search, setSearch] = useState('');

  // Пересчитывается только при изменении products или search
  const filtered = useMemo(
    () => products.filter(p => p.name.toLowerCase().includes(search.toLowerCase())),
    [products, search]
  );

  return (
    <>
      <input value={search} onChange={e => setSearch(e.target.value)} />
      {filtered.map(p => <div key={p.id}>{p.name}</div>)}
    </>
  );
}

Пример: useCallback — стабильная ссылка для дочернего компонента

import { useCallback, useState, memo } from 'react';

// Дочерний компонент, обёрнутый в memo — не ре-рендерится при тех же пропсах
const Button = memo(({ onClick, label }: { onClick: () => void; label: string }) => {
  console.log('Button render:', label);
  return <button onClick={onClick}>{label}</button>;
});

function Counter() {
  const [count, setCount] = useState(0);
  const [other, setOther] = useState(0);

  // Без useCallback — новая ссылка на каждый рендер, memo не спасёт
  const handleIncrement = useCallback(() => {
    setCount(c => c + 1);
  }, []); // зависимостей нет — функция создаётся один раз

  return (
    <>
      <p>Count: {count}, Other: {other}</p>
      <Button onClick={handleIncrement} label="+1" />
      <button onClick={() => setOther(o => o + 1)}>Изменить other</button>
    </>
  );
}

Пример: Эквивалентность useCallback и useMemo

import { useMemo, useCallback } from 'react';

function Example({ id }: { id: number }) {
  // Эти две записи эквивалентны:
  const handleA = useCallback(() => console.log(id), [id]);
  const handleB = useMemo(() => () => console.log(id), [id]);

  // handleA === handleB по поведению
  // useCallback просто удобнее читается для функций
}

Типичные ошибки

Путают хуки и считают, что useMemo тоже мемоизирует функцию, а не её результат

Оборачивают всё подряд в useMemo/useCallback «на всякий случай», не профилируя реальные проблемы

Забывают указать нужные зависимости или указывают лишние, нарушая корректность мемоизации

Используют useCallback для функций, которые не передаются дальше и не влияют на повторные рендеры

Не используют useCallback там, где это действительно нужно — при передаче коллбэков в React.memo-компоненты

Лучшие курсы по теме

изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Feature-Sliced Design

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.5
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее