← Назад к вопросам

Для чего используются хуки useMemo и useCallback в React?

1.7 Middle🔥 211 комментариев
#React

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Назначение хуков useMemo и useCallback в React

Оба хука — useMemo и useCallback — являются инструментами для оптимизации производительности в React-приложениях. Они помогают избежать лишних вычислений и ререндеров компонентов, сохраняя (мемоизируя) значения между рендерами. Однако их применение и внутренняя механика различаются.

useMemo: Мемоизация вычисляемых значений

useMemo используется для сохранения результата тяжёлых вычислений между рендерами. Он принимает функцию-создатель (create) и массив зависимостей (deps). Хук возвращает мемоизированное значение и пересчитывает его только при изменении одной из зависимостей.

import React, { useMemo } from 'react';

const ExpensiveComponent = ({ items, filterTerm }) => {
  // Фильтрация будет выполнена только при изменении items или filterTerm
  const filteredItems = useMemo(() => {
    console.log('Выполняется дорогая фильтрация...');
    return items.filter(item => item.name.includes(filterTerm));
  }, [items, filterTerm]); // Зависимости

  return (
    <ul>
      {filteredItems.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
};

Ключевые аспекты useMemo:

  • Предотвращение лишних вычислений: Затратные операции (фильтрация больших массивов, сложные математические вычисления, преобразование данных) не будут выполняться на каждом рендере.
  • Ссылочная стабильность: Возвращаемое значение сохраняет ссылочную идентичность, если зависимости не изменились. Это особенно важно при передаче значения в качестве пропса в дочерние компоненты, обёрнутые в React.memo, так как предотвращает их ненужные ререндеры.
  • Не гарантирует единственный вызов: Это оптимизация, а не гарантия. React может в некоторых случаях "забывать" мемоизированные значения.

useCallback: Мемоизация функций

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

import React, { useState, useCallback } from 'react';
import ChildComponent from './ChildComponent';

const ParentComponent = () => {
  const [count, setCount] = useState(0);

  // Без useCallback при каждом рендере ParentComponent
  // создаётся НОВАЯ функция, что вызовет ререндер ChildComponent
  const handleClick = useCallback(() => {
    console.log('Клик залогирован, count:', count);
    // Важно: здесь count зафиксирован на значении,
    // которое было при создании функции (если не указан в deps).
  }, []); // Пустой массив зависимостей: функция создаётся единожды

  // Более корректный вариант, если нужен актуальный count
  const handleClickWithDep = useCallback(() => {
    console.log('Актуальный count:', count);
    // Какая-то логика с count
  }, [count]); // Функция пересоздаётся при изменении count

  return (
    <div>
      <button onClick={() => setCount(c => c + 1)}>Увеличить: {count}</button>
      {/* ChildComponent будет ререндериться только если изменится сама функция handleClick */}
      <ChildComponent onClick={handleClick} />
    </div>
  );
};

Ключевые аспекты useCallback:

  • Оптимизация дочерних компонентов: Основная цель — передача стабильной ссылки на колбэк в дочерние компоненты, которые используют React.memo или зависят от сравнения ссылок на пропсы (например, в useEffect внутри дочернего компонента).
  • Зависимости (deps): Крайне важно правильно указывать зависимости. Пустой массив [] означает, что функция будет создана один раз и будет "видеть" только начальные значения переменных из замыкания. Если внутри функции используется состояние (state) или пропс, их обычно нужно добавлять в массив зависимостей.
  • Не ускоряет создание функции: Само по себе создание функции — быстрая операция. Проблема, которую решает useCallback, — не в скорости создания, а в стабильности ссылки, влияющей на ререндеры дочерних компонентов и зависимости эффектов.

Сравнение и общие принципы использования

АспектuseMemouseCallback
Что мемоизируетРезультат любой функции (значение: объект, массив, примитив)Саму функцию
СинтаксисuseMemo(() => computeValue, deps)useCallback(fn, deps)
ЭквивалентuseCallback(() => fn, deps) — это useMemo(() => fn, deps)useCallback(fn, deps) — это useMemo(() => fn, deps)
Основное назначениеИзбежать повторных тяжёлых вычислений, обеспечить стабильность ссылки на сложный объектОбеспечить стабильность ссылки на функцию, передаваемую вниз по дереву компонентов

Важные предостережения:

  1. Не использовать повсеместно "на всякий случай". Индексная оптимизация может привести к обратному эффекту: потреблять больше памяти на хранение старых значений и усложнять код. Сначала измеряйте производительность с помощью React DevTools Profiler.
  2. Проблема "устаревшего замыкания" (stale closure). Неправильно указанные зависимости в useCallback могут привести к использованию устаревших значений переменных внутри функции.
  3. useMemo для создания функций. Для мемоизации функции технически можно использовать и useMemo: useMemo(() => () => {...}, deps). Но useCallback — это синтаксический сахар, специально предназначенный для этого случая, и его код читается лучше.

Правило применения: Используйте useMemo для сохранения ресурсоёмких результатов вычислений или сложных объектов, а useCallback — когда необходимо предотвратить ререндер дочернего компонента, который принимает функцию в качестве пропса и обёрнут в React.memo. В остальных случаях часто можно обойтись без этих хуков.

Для чего используются хуки useMemo и useCallback в React? | PrepBro