Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

useMemo и useCallback

При каждой отрисовке React заново выполняет весь код компонента: вычисления, создание функций. Обычно это быстро и незаметно. Хуки useMemo и useCallback позволяют запомнить (мемоизировать) значение или функцию и пересоздавать их, только когда изменились зависимости. Это оптимизация: без неё код работает так же, просто иногда медленнее.

useMemo: запомнить вычисление

Добавим в блог поиск по статьям. Фильтрация выполняется при каждой отрисовке — даже когда меняется не поисковая строка, а, например, тема оформления. В src/components/PostSearch.jsx:

// src/components/PostSearch.jsx
import { useMemo, useState } from 'react';
 
export default function PostSearch({ posts }) {
  const [query, setQuery] = useState('');
 
  const found = useMemo(
    () => posts.filter((post) => post.title.includes(query.toLowerCase())),
    [posts, query],
  );
 
  return (
    <>
      <input value={query} onChange={(event) => setQuery(event.target.value)} />
      <p>Найдено: {found.length}</p>
    </>
  );
}

Первый аргумент — функция, которая вычисляет значение, второй — зависимости, как у useEffect. Пока posts и query прежние, React вернёт сохранённый массив, не вызывая filter. Введите «qui» — счётчик покажет, сколько заголовков содержат эти буквы.

useCallback: запомнить функцию

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

// src/App.jsx
import { useCallback, useState } from 'react';
 
export default function App() {
  const [todos, setTodos] = useState([]);
 
  const handleDelete = useCallback((id) => {
    setTodos((current) => current.filter((todo) => todo.id !== id));
  }, []);
 
  return <TodoList todos={todos} onDelete={handleDelete} />;
}

Сеттер вызван с функцией, поэтому todos в зависимостях не нужен, и handleDelete создаётся один раз. Это важно, только если TodoList обёрнут в memo (урок о производительности) или функция стоит в зависимостях эффекта.

Когда это нужно

СитуацияНужна мемоизация?
Фильтр или сумма по сотне элементовНет
Тяжёлое вычисление: тысячи элементов, сложная сортировкаuseMemo
Функция передаётся компоненту, обёрнутому в memouseCallback
Функция или объект в зависимостях useEffectuseMemo или useCallback
Обычный обработчик для кнопкиНет

Сначала пишите без них. Если страница тормозит, найдите медленное место инструментом Profiler в React Developer Tools и только тогда добавляйте мемоизацию.

React Compiler

Существует React Compiler — инструмент сборки, который сам расставляет мемоизацию в компонентах. В проектах, где он подключён, useMemo и useCallback почти не пишут вручную. В обычном шаблоне Vite он не включён, поэтому знать эти хуки всё равно полезно — они часто встречаются в чужом коде.

Частая ошибка

Забытая зависимость заставляет useMemo возвращать устаревшее значение:

// Неправильно: query не в зависимостях, поиск не реагирует на ввод.
const found = useMemo(
  () => posts.filter((post) => post.title.includes(query)),
  [posts],
);

Перечисляйте все значения, которые используются внутри функции. Линтер проекта подскажет, если какое-то пропущено.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.