Для чего используется хук useCallback в React
Что проверяют
Понимаете ли вы, что он нужен ради стабильности ссылки, а не ради скорости самой функции.
Как отвечать
useCallback возвращает ту же ссылку на функцию между рендерами, пока не изменились зависимости. Без него функция создаётся заново на каждом рендере, и её ссылка каждый раз новая.
Сама по себе эта пересоздание почти ничего не стоит. Проблема в потребителях ссылки, и хук нужен только в трёх случаях:
- дочерний компонент обёрнут в
React.memo. Он сравнивает пропсы поверхностно, и новая функция каждый раз делает мемоизацию бесполезной; - функция стоит в зависимостях
useEffect. Новая ссылка на каждом рендере перезапускает эффект бесконечно; - функция уходит в другой
useMemo,useCallbackили во внешнюю подписку, где важна стабильность.
Если ничего из этого нет, useCallback только добавляет код и работу: он и сам не бесплатен, потому что хранит зависимости и сравнивает их.
Связь с useMemo прямая: useCallback(fn, deps) это то же самое, что useMemo(() => fn, deps). Первый мемоизирует функцию, второй значение.
Ловушки:
- неполный массив зависимостей даёт устаревшее замыкание: функция помнит состояние того рендера, в котором была создана. Правило зависимостей из линтера подавлять нельзя;
useCallbackне спасает, если в тот же компонент передаётся объектный литерал или массив, создаваемые заново. Мемоизировать надо все пропсы, иначеReact.memoвсё равно не сработает;- обычная альтернатива: передавать вниз не колбэк, а
dispatchизuseReducer, он стабилен по определению.
Полезно упомянуть, что компилятор React в версии 19 расставляет такую мемоизацию автоматически, и ручных вызовов становится меньше.
Где спотыкаются
Оборачивают в useCallback каждую функцию подряд.
Ставят его без React.memo на дочернем компоненте и не получают эффекта.
Урезают массив зависимостей, чтобы «не пересоздавалось», и ловят устаревшие значения.
Считают, что хук ускоряет саму функцию.