Как работают useMemo и useCallback для оптимизации производительности React-компонентов
Что проверяют
Знаете ли вы, что оба хука кэшируют между рендерами по массиву зависимостей, и понимаете ли, когда это оправдано. Почти всегда следом спрашивают, в чём разница.
Как отвечать
Разница одна: useMemo запоминает результат вызова функции, а useCallback саму функцию.
const filtered = useMemo(() => items.filter(byQuery), [items, query]);
const onSelect = useCallback((id) => setSelected(id), []);useCallback(fn, deps) это в точности useMemo(() => fn, deps).
Когда применять:
useMemo: когда вычисление действительно тяжёлое (фильтрация и сортировка тысяч элементов) или когда результат уходит в пропсы мемоизированного компонента и важна стабильная ссылка;useCallback: когда функция уходит в пропсы компонента подmemoили в зависимости другого хука.
Без memo у ребёнка стабильная ссылка обычно ничего не даёт: родитель перерисуется, ребёнок тоже, вне зависимости от того, новая функция или старая.
Где спотыкаются
Считают, что мемоизация ускоряет сама по себе. Хук не бесплатен: React хранит зависимости и сравнивает их каждый рендер. На простом вычислении это медленнее прямого вызова.
Забывают зависимости или указывают их неверно: тогда внутри замыкания остаётся старое значение, и появляется баг, который сложно поймать.
Не знают, что React гарантирует кэш не навсегда: реализация вправе выбросить запомненное. Полагаться на useMemo как на хранилище состояния нельзя.
Стоит упомянуть, что в React 19 появился компилятор, который расставляет мемоизацию сам, и ручных хуков в новом коде становится меньше.