Какие проблемы решает useCallback?
Хуки
useCallback решает одну проблему: функции, создаваемые в рендере, каждый раз новые по ссылке, и это ломает оптимизации, завязанные на сравнение ссылок.
Проблема 1: сломанный React.memo
const Parent = () => {
const [text, setText] = useState('');
const handleDelete = (id) => api.delete(id); // новая ссылка каждый рендер
return (
<>
<input value={text} onChange={(e) => setText(e.target.value)} />
<HeavyList onDelete={handleDelete} /> {/* React.memo бесполезен */}
</>
);
};Каждый ввод символа перерендеривает HeavyList: props «изменились», ведь handleDelete — новая функция. С useCallback ссылка стабильна, и React.memo срабатывает:
const handleDelete = useCallback((id) => api.delete(id), []);Проблема 2: лишние срабатывания эффектов
const loadData = useCallback(() => fetch(`/api/${page}`), [page]);
useEffect(() => { loadData(); }, [loadData]); // перезапуск только при смене pageБез useCallback функция в зависимостях эффекта означала бы перезапуск на каждый рендер.
Проблема 3: стабильные колбэки для кастомных хуков
Кастомные хуки, принимающие функции (обработчики, колбэки подписок), полагаются на стабильность ссылок в своих зависимостях — useCallback это обеспечивает.
Что useCallback НЕ решает
- Не ускоряет сам вызов функции и не «кеширует» её результат (это useMemo).
- Бесполезен, если получатель не мемоизирован и не использует функцию в зависимостях.
- Не спасает от устаревших замыканий — наоборот, требует аккуратных зависимостей.
Коротко для собеседования
useCallback стабилизирует ссылку на функцию между рендерами. Решает три задачи: React.memo-дети не перерендериваются из-за «новых» колбэков, эффекты с функцией в зависимостях не перезапускаются каждый рендер, кастомные хуки получают стабильные колбэки. Без мемоизированного потребителя — не нужен.