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

Зачем нужны deps в useCallback?

1.2 Junior🔥 131 комментариев
#React

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

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

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

Зачем нужны deps в useCallback?

useCallback — это хук для мемоизации функций. Массив зависимостей (deps) в useCallback критически важен: он определяет, когда функция должна быть пересоздана. Без правильно настроенных зависимостей useCallback просто не работает.

Как работает useCallback без deps

Без массива зависимостей каждый рендер компонента создаёт новую функцию:

function Counter() {
  const [count, setCount] = useState(0);
  
  // Без deps - функция пересоздаётся каждый раз
  const increment = useCallback(() => {
    setCount(count + 1);
  }); // Плохо!
  
  return <ChildComponent onIncrement={increment} />;
}

function ChildComponent({ onIncrement }) {
  // onIncrement каждый раз новая функция
  // Даже если компонент обёрнут в React.memo, эффект не будет
  return <button onClick={onIncrement}>Increment</button>;
}

Это O(n) лишних рендеров детских компонентов.

Зачем нужен массив deps

Массив зависимостей говорит React-у: "Пересоздай функцию только если эти переменные изменились". Это оптимизирует производительность:

function Counter() {
  const [count, setCount] = useState(0);
  const [name, setName] = useState('Alice');
  
  // Функция пересоздаётся только если count изменился
  const increment = useCallback(() => {
    setCount(count + 1);
  }, [count]); // deps указан!
  
  // Функция пересоздаётся только если name изменился
  const changeName = useCallback((newName) => {
    setName(newName);
  }, []);
  
  return (
    <div>
      <ChildComponent onIncrement={increment} onChangeName={changeName} />
      <p>Name: {name}</p>
    </div>
  );
}

function ChildComponent({ onIncrement, onChangeName }) {
  // Обёрнут в React.memo - не рендерится без причины
  console.log('Child re-rendered');
  
  return (
    <>
      <button onClick={onIncrement}>Increment</button>
      <button onClick={() => onChangeName('Bob')}>Change Name</button>
    </>
  );
}

const ChildComponent = React.memo(ChildComponent);

Правило: включай все зависимости

Все переменные, используемые внутри функции, должны быть в массиве deps. Это правило Hooks (ESLint плагин warn о нарушениях):

function Example() {
  const [count, setCount] = useState(0);
  const [multiplier, setMultiplier] = useState(2);
  
  // Неправильно: забыли multiplier в deps
  const multiply = useCallback(() => {
    return count * multiplier; // multiplier из замыкания!
  }, [count]); // Опасно!
  
  // Правильно: указаны все зависимости
  const multiply = useCallback(() => {
    return count * multiplier;
  }, [count, multiplier]);
  
  return <div>{multiply()}</div>;
}

Если забыть зависимость:

  • Функция будет использовать старые значения переменных
  • Это ведёт к race conditions и bugs
  • ESLint с плагином eslint-plugin-react-hooks поймёт это

Когда использовать пустой массив deps []

Пустой массив deps [] означает: "Функция никогда не меняется":

function Parent() {
  // Эта функция создаётся один раз при монтировании
  const handleClick = useCallback(() => {
    console.log('Clicked');
  }, []); // Пустой массив - функция никогда не меняется
  
  return <Child onClick={handleClick} />;
}

Это полезно, если функция не зависит от никаких переменных компонента.

Практический пример: запросы к API

function UsersList() {
  const [userId, setUserId] = useState(null);
  const [data, setData] = useState(null);
  
  // Создаём функцию один раз - она не зависит от внешних переменных
  const fetchUser = useCallback(async (id) => {
    const response = await fetch(`/api/users/${id}`);
    return response.json();
  }, []); // []!
  
  // Эффект зависит от userId и fetchUser
  useEffect(() => {
    if (userId) {
      fetchUser(userId).then(setData);
    }
  }, [userId, fetchUser]);
  
  return (
    <div>
      <input
        type="number"
        value={userId}
        onChange={(e) => setUserId(Number(e.target.value))}
      />
      {data && <div>{data.name}</div>}
    </div>
  );
}

Когда useCallback не нужен

  1. Функция простая и быстрая - создание новой функции дешевле мемоизации
  2. Функция не передаётся в Child komponenty - нет смысла в мемоизации
  3. Child komponenty не обёрнуты в React.memo - useCallback не поможет
// Без useCallback - нормально
function Form() {
  const [email, setEmail] = useState('');
  
  // Простая функция - useCallback излишний
  const handleChange = (e) => {
    setEmail(e.target.value);
  };
  
  return <input onChange={handleChange} />;
}

Резюме: Значение deps

  • deps = явный сигнал React-у когда пересоздавать функцию
  • Правильные deps = корректная работа приложения
  • Забытые deps = баги с устаревшими значениями
  • Пустой deps [] = функция стабильна на весь lifetime компонента
  • Используй ESLint плагин для проверки deps

Вывод: Массив зависимостей в useCallback - не просто фичура, это основной механизм управления жизненным циклом функции. Правильное использование deps критично для производительности и корректности React-приложений.