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

Когда нужно было прокидывать функцию в массив зависимостей?

1.7 Middle🔥 111 комментариев
#JavaScript Core

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

🐱
deepseek-v3.2PrepBro AI4 апр. 2026 г.(ред.)

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

Когда функция должна быть в массиве зависимостей?

Прокидывание функции в массив зависимостей в React (в хуках useEffect, useCallback, useMemo) — один из ключевых аспектов работы с зависимостями, который напрямую влияет на стабильность ссылок и предотвращение бесконечных циклов.

Основные случаи, когда функция должна быть в зависимостях

1. Функция определена внутри компонента (в области видимости компонента)

Если функция объявлена внутри тела функционального компонента, она создаётся заново при каждом рендере, что приводит к изменению её ссылки. Если такая функция используется внутри useEffect, её необходимо добавить в массив зависимостей, иначе эффект будет работать со "старой" версией функции, которая не имеет актуальных значений из замыкания.

function MyComponent({ userId }) {
  const [data, setData] = useState(null);
  
  // Функция объявлена внутри компонента — создаётся заново при каждом рендере
  const fetchData = async () => {
    const response = await fetch(`/api/users/${userId}`);
    setData(await response.json());
  };
  
  useEffect(() => {
    fetchData();
  }, [fetchData]); // fetchData должна быть в зависимостях, так как использует userId
  
  return <div>{data ? data.name : 'Loading...'}</div>;
}

Проблема: В этом примере fetchData будет меняться при каждом рендере, что вызовет повторный запуск useEffect каждый раз — потенциально приводя к бесконечному циклу.

2. Функция использует пропсы или состояние внутри замыкания

Даже если функция не меняет свою логику, но использует внутри себя значения из пропсов или состояния, её необходимо добавлять в зависимости, чтобы эффект "видел" актуальные значения.

useEffect(() => {
  const handleResize = () => {
    console.log('Current user ID:', userId); // Используется пропс userId
  };
  
  window.addEventListener('resize', handleResize);
  return () => window.removeEventListener('resize', handleResize);
}, [userId]); // userId должен быть в зависимостях, так как используется внутри функции

Как правильно управлять зависимостями функций

Решение 1: Перенос функции внутрь эффекта

Если функция используется только внутри одного эффекта, её можно объявить непосредственно в нём — тогда она будет зависеть только от зависимостей этого эффекта.

useEffect(() => {
  const fetchData = async () => {
    const response = await fetch(`/api/users/${userId}`);
    setData(await response.json());
  };
  
  fetchData();
}, [userId]); // Только userId как зависимость

Решение 2: Использование useCallback

Для функций, которые должны сохранять стабильную ссылку между рендерами, используется хук useCallback. Он мемоизирует функцию и обновляет её только при изменении указанных зависимостей.

const fetchData = useCallback(async () => {
  const response = await fetch(`/api/users/${userId}`);
  setData(await response.json());
}, [userId]); // Функция обновится только при изменении userId

useEffect(() => {
  fetchData();
}, [fetchData]); // Теперь useEffect сработает только при реальном изменении fetchData

Решение 3: Вынос функции за пределы компонента

Если функция не зависит от пропсов или состояния компонента, её можно вынести вне компонента — тогда она всегда будет иметь одну и ту же ссылку.

// Вынесенная наружу функция — не зависит от скоупа компонента
const formatDate = (date) => new Intl.DateTimeFormat('ru-RU').format(date);

function MyComponent() {
  useEffect(() => {
    console.log(formatDate(new Date()));
  }, []); // formatDate не нужно добавлять в зависимости
  // ...
}

Ключевые правила и подводные камни

  • Правило реакта: Все значения, используемые внутри эффекта (включая функции), должны быть в массиве зависимостей. Это включает пропсы, состояние, контекст и функции из скоупа компонента.
  • Исключение: Функции из useRef, setState (из useState), dispatch (из useReducer) — React гарантирует их стабильность, их можно не добавлять в зависимости.
  • Ошибка линтера: Если используется eslint-plugin-react-hooks, линтер укажет на пропущенные зависимости — этому стоит следовать, чтобы избежать скрытых багов.
  • Бесконечные циклы: Добавление функции, которая меняется каждый рендер, в зависимости useEffect без мемоизации вызовет бесконечный цикл.
  • useMemo: Аналогичные принципы применяются для useMemo — если функция используется внутри мемоизированного значения, её также нужно добавлять в зависимости или мемоизировать.

Пример комплексного подхода

function UserDashboard({ userId, onSuccess }) {
  const [data, setData] = useState(null);
  
  // Мемоизированная функция, обновляется только при изменении userId
  const fetchUserData = useCallback(async () => {
    const response = await fetch(`/api/users/${userId}`);
    const result = await response.json();
    setData(result);
    onSuccess(result); // Используем пропс onSuccess
  }, [userId, onSuccess]); // Все зависимости явно указаны
  
  // Эффект для загрузки данных при монтировании и изменении fetchUserData
  useEffect(() => {
    fetchUserData();
  }, [fetchUserData]);
  
  // Другой эффект, использующий ту же функцию, но с другими зависимостями
  useEffect(() => {
    const interval = setInterval(fetchUserData, 30000);
    return () => clearInterval(interval);
  }, [fetchUserData]);
  
  return <div>{data ? data.name : 'Loading...'}</div>;
}

Итог: Функция должна быть в массиве зависимостей всегда, когда она создана внутри компонента и используется внутри хука с зависимостями. Оптимальная стратегия — использовать useCallback для мемоизации таких функций с явным указанием их зависимостей, что предотвращает лишние ререндеры и сохраняет логику работы эффектов предсказуемой.