← Назад к вопросам

Для чего используется UseCllback?

2.2 Middle🔥 181 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

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 когда:

  1. Функция передаётся как prop дочернему компоненту, обёрнутому в React.memo
  2. Функция используется как зависимость в других Hooks (useEffect, useMemo)
  3. Функция используется в event listeners, которые нужны для оптимизации
  4. Есть проблемы с производительностью, вызванные пересозданием функций

Не используй 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 приложений. Используй его разумно: не везде нужна мемоизация, но в правильных местах она может значительно улучшить производительность.

Для чего используется UseCllback? | PrepBro