useCallback: мемоизация функций
useCallback возвращает мемоизированную версию функции, которая меняется только при изменении зависимостей.
Синтаксис
const fn = useCallback(() => {
doSomething(a, b);
}, [a, b]);
Без зависимостей — функция создаётся один раз:
const fn = useCallback(() => {
console.log('один раз');
}, []);
Зачем нужен
В React функции создаются заново при каждом рендере. Если функция передаётся в дочерний компонент как пропс, дочерний компонент перерисовывается, даже если логика не изменилась.
function Parent() {
const handleClick = () => console.log('click');
return <Child onClick={handleClick} />;
}
При каждом рендере Parent создаёт новую handleClick, и Child обновляется.
С useCallback:
function Parent() {
const handleClick = useCallback(() => console.log('click'), []);
return <Child onClick={handleClick} />;
}
Теперь функция стабильна между рендерами.
Когда работает
useCallback полезен, если дочерний компонент мемоизирован через React.memo:
const Child = React.memo(function Child({ onClick }: { onClick: () => void }) {
console.log('Child render');
return <button onClick={onClick}>OK</button>;
});
Без React.memo useCallback не даёт эффекта: Child всё равно перерисовывается при перерисовке родителя.
В зависимостях useEffect
Если функция используется в useEffect, её тоже нужно стабилизировать:
const loadData = useCallback(async () => {
const res = await fetch(`/api/users/${userId}`);
setUser(await res.json());
}, [userId]);
useEffect(() => {
loadData();
}, [loadData]);
Без useCallback функция меняется каждый рендер, и эффект сработает бесконечно.
Функциональное обновление
Если функция использует предыдущее состояние — используй функциональную форму, чтобы уменьшить зависимости:
// Зависит от count
const increment = useCallback(() => {
setCount(count + 1);
}, [count]);
// Не зависит от count
const increment = useCallback(() => {
setCount((c) => c + 1);
}, []);
useCallback vs useMemo
- useCallback(fn, deps) эквивалентно useMemo(() => fn, deps)
useCallback— для функцийuseMemo— для значений
Не оптимизируй преждевременно
Оборачивать в useCallback каждую функцию — плохая идея. Это усложняет код и добавляет работу. Используй только там, где это реально нужно: для мемоизированных компонентов и зависимостей эффектов.
Итоги
useCallbackмемоизирует функцию- Полезен с
React.memoи в зависимостях - Без мемоизации дочернего компонента не даёт эффекта
- Функциональное обновление уменьшает зависимости
- Не оборачивай всё подряд