Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
useCallback Hook в React
useCallback — это React Hook, который позволяет оптимизировать производительность приложения путём мемоизации функций. Он особенно полезен в сценариях, где функции передаются как props дочерним компонентам.
Основная цель useCallback
Основная проблема: каждый раз, когда компонент переренериется, все функции, определённые внутри него, пересоздаются в памяти. Это приводит к тому, что даже если логика функции не изменилась, её ссылка (reference) становится другой.
function Parent() {
// Эта функция пересоздаётся при каждом рендере
const handleClick = () => {
console.log("Clicked");
};
return <Child onClick={handleClick} />;
}
В примере выше, при каждом рендере Parent, handleClick получает новую ссылку в памяти, что заставляет Child также переренериться (даже если он обёрнут в React.memo).
Решение с useCallback
import { useCallback } from "react";
function Parent() {
// Функция мемоизируется и пересоздаётся только если
// изменяются зависимости в массиве dependency array
const handleClick = useCallback(() => {
console.log("Clicked");
}, []); // Пустой массив = функция никогда не пересоздаётся
return <Child onClick={handleClick} />;
}
Синтаксис useCallback
const memoizedCallback = useCallback(
(param) => {
// Логика функции
doSomething(param);
},
[dependency1, dependency2] // Массив зависимостей
);
- Первый аргумент: функция для мемоизации
- Второй аргумент: массив зависимостей (dependency array)
Функция пересоздаётся только если одна из зависимостей изменится.
Практические примеры
Пример 1: Передача функции дочернему компоненту
interface ChildProps {
onSubmit: (data: string) => void;
}
const Child = React.memo(({ onSubmit }: ChildProps) => {
return <button onClick={() => onSubmit("data")}>Submit</button>;
});
function Parent() {
const [count, setCount] = useState(0);
// Без useCallback — Child переренериться каждый раз
const handleSubmit = useCallback((data: string) => {
console.log(data);
}, []); // Зависимостей нет
return (
<>
<button onClick={() => setCount(count + 1)}>Increment</button>
<Child onSubmit={handleSubmit} />
</>
);
}
Пример 2: С зависимостями
function Parent() {
const [userId, setUserId] = useState(1);
// fetchUser пересоздаётся только если userId изменится
const fetchUser = useCallback(() => {
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => console.log(data));
}, [userId]); // userId в массиве зависимостей
return (
<>
<button onClick={() => setUserId(userId + 1)}>Next User</button>
<button onClick={fetchUser}>Fetch</button>
</>
);
}
Пример 3: В обработчиках форм
function LoginForm() {
const [formData, setFormData] = useState({ email: "", password: "" });
const handleChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setFormData(prev => ({ ...prev, [name]: value }));
},
[] // Зависимостей нет
);
const handleSubmit = useCallback(
async (e: React.FormEvent) => {
e.preventDefault();
const response = await fetch("/api/login", {
method: "POST",
body: JSON.stringify(formData)
});
// Обработка ответа
},
[formData] // Зависит от formData
);
return (
<form onSubmit={handleSubmit}>
<input name="email" onChange={handleChange} />
<input name="password" onChange={handleChange} />
<button type="submit">Login</button>
</form>
);
}
Когда использовать useCallback
Используй useCallback когда:
- Функция передаётся как prop дочернему компоненту, обёрнутому в React.memo
- Функция используется как зависимость в других Hooks (useEffect, useMemo)
- Функция используется в event listeners, которые нужны для оптимизации
- Есть проблемы с производительностью, вызванные пересозданием функций
Не используй useCallback когда:
- Функция используется только внутри компонента
- Нет проблем с производительностью
- Дополнительная мемоизация замедляет приложение
Отличие от useMemo
// useCallback мемоизирует функцию
const memoCallback = useCallback(() => expensive(), [deps]);
// useMemo мемоизирует результат вычисления
const memoValue = useMemo(() => expensive(), [deps]);
// useCallback(fn, deps) эквивалентно useMemo(() => fn, deps)
Важные моменты
1. Быстродействие: useCallback имеет собственную стоимость. Иногда проще пересоздать функцию, чем мемоизировать.
2. Правильные зависимости: Массив зависимостей ДОЛЖЕН быть полным.
// НЕПРАВИЛЬНО — может привести к ошибкам
const handleClick = useCallback(() => {
fetchData(userId); // userId используется, но не в массиве!
}, []); // Ошибка!
// ПРАВИЛЬНО
const handleClick = useCallback(() => {
fetchData(userId);
}, [userId]); // userId добавлен в зависимости
3. Вместе с React.memo: useCallback максимально эффективен с мемоизированными дочерними компонентами.
const MemoChild = React.memo(Child);
Заключение
useCallback — мощный инструмент для оптимизации React приложений. Используй его разумно: не везде нужна мемоизация, но в правильных местах она может значительно улучшить производительность.