Ответ
Первый аргумент useCallback — это функция, которую нужно мемоизировать (запомнить).
Основная цель: предотвратить создание новой идентичной функции при каждом рендере компонента React. Это критично для оптимизации, когда:
- Функция передаётся в качестве пропса (
onClick,onChange) дочернему компоненту, обёрнутому вReact.memo. БезuseCallbackдочерний компонент будет перерисовываться каждый раз, потому что получает новую ссылку на функцию, даже если её логика не изменилась. - Функция является зависимостью других хуков, например,
useEffect. Её стабильная ссылка предотвращает лишние срабатывания эффекта.
Пример:
import React, { useState, useCallback } from 'react';
const ExpensiveChild = React.memo(({ onAction }) => {
console.log('Child rendered!');
return <button onClick={onAction}>Click me</button>;
});
function ParentComponent() {
const [count, setCount] = useState(0);
// Без useCallback эта функция создавалась бы заново при каждом обновлении count
const handleClick = useCallback(() => {
console.log('Action performed, count is:', count);
}, [count]); // Функция пересоздаётся только при изменении `count`
return (
<div>
<button onClick={() => setCount(c => c + 1)}>Increment: {count}</button>
<ExpensiveChild onAction={handleClick} />
</div>
);
}
Ключевой момент: useCallback возвращает мемоизированную версию функции, а не мемоизирует её результат (это делает useMemo). Второй аргумент — массив зависимостей — определяет, когда функцию нужно пересоздать.
Ответ 18+ 🔞
А, ну это же классика, ёпта! Смотри, первый аргумент в useCallback — это, по сути, сама функция, которую тебе надо законсервировать, чтобы она не протухла.
Зачем это вообще надо, спросишь? Чтобы React не создавал новую, абсолютно такую же функцию каждый раз, когда компонент перерисовывается. Это как если бы ты каждый день покупал новую одинаковую кружку, а старую выкидывал — зачем, если старая ещё целая? Особенно критично это в двух случаях:
- Ты эту функцию прокидываешь пропсом в дочерний компонент, который обёрнут в
React.memo. БезuseCallbackэтот ребёнок будет охуевать и перерисовываться на каждом чихе, потому что ему каждый раз прилетает "новая" функция, хоть и делает она то же самое. - Эта функция сидит в зависимостях других хуков, типа
useEffect. Если ссылка на неё прыгает, эффект будет срабатывать как ненормальный, а тебе этого, скорее всего, не надо.
Смотри, как это выглядит в деле:
import React, { useState, useCallback } from 'react';
const ExpensiveChild = React.memo(({ onAction }) => {
console.log('Child rendered!'); // Этот лог не будет спамить без причины
return <button onClick={onAction}>Click me</button>;
});
function ParentComponent() {
const [count, setCount] = useState(0);
// Вот она, магия. Без useCallback эта штука рождалась бы заново при каждом клике на Increment.
const handleClick = useCallback(() => {
console.log('Action performed, count is:', count);
}, [count]); // Ключевой момент! Функцию перепечатают только если `count` изменится.
return (
<div>
<button onClick={() => setCount(c => c + 1)}>Increment: {count}</button>
<ExpensiveChild onAction={handleClick} />
</div>
);
}
Самое главное не перепутать: useCallback запоминает ссылку на саму функцию, а не то, что она возвращает. За результат отвечает useMemo, это другая история. А второй аргумент — это массив зависимостей, твоя памятка для Реакта: "Чувак, пересоздай функцию только тогда, когда вот эти штуки поменяются". Если передать пустой массив [], то функция создастся один раз и будет жить вечно, как скала, не обращая внимания ни на что вокруг.