Как dependency array влияет на поведение useEffect?

JuniorReact · Frontend·Обновлено 26 июля 2026
Коротко
Dependency array определяет, при каких изменениях состояния или пропсов будет повторно вызван эффект. Если массив пустой — эффект выполняется один раз после монтирования, если массив отсутствует — при каждом рендере.

Что такое dependency array

Dependency array (массив зависимостей) — это второй аргумент хука useEffect, который контролирует, когда эффект будет запущен повторно.

Варианты поведения

1. Без массива зависимостей

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

useEffect(() => {
  // Выполняется после каждого рендера
  console.log('компонент обновился');
});

Это редко бывает нужным и может вызвать проблемы с производительностью.

2. Пустой массив зависимостей

Эффект выполняется один раз после первого монтирования компонента и больше не повторяется.

useEffect(() => {
  // Выполняется только при монтировании
  fetchUserData();

  return () => {
    // Функция очистки вызывается при размонтировании
    cleanup();
  };
}, []);

Аналог componentDidMount из классовых компонентов.

3. Массив с зависимостями

Эффект запускается при монтировании и каждый раз, когда изменяется хотя бы одна из перечисленных зависимостей.

const [userId, setUserId] = useState(1);

useEffect(() => {
  // Вызывается при монтировании и при каждом изменении userId
  fetchUserById(userId);
}, [userId]);

Как React сравнивает зависимости

React использует алгоритм Object.is для сравнения значений между рендерами. Это означает, что:

  • Примитивы (числа, строки, булевы) сравниваются по значению
  • Объекты и массивы сравниваются по ссылке
useEffect(() => {
  // Этот эффект будет вызываться на каждом рендере,
  // так как объект options создаётся заново при каждом рендере
  fetchData(options);
}, [options]); // options = { page: 1 } — новая ссылка каждый раз

Чтобы избежать этого, нужно стабилизировать объект через useMemo или вынести его за пределы компонента.

Функция очистки

При смене зависимостей React сначала вызывает функцию очистки предыдущего эффекта, затем запускает новый.

useEffect(() => {
  const subscription = subscribe(channelId);

  return () => {
    // Вызывается перед следующим запуском эффекта
    // или при размонтировании компонента
    subscription.unsubscribe();
  };
}, [channelId]);

Правило exhaustive-deps

ESLint-плагин eslint-plugin-react-hooks предупреждает, если в эффекте используются переменные, не указанные в dependency array. Игнорировать эти предупреждения не рекомендуется — это обычно признак логической ошибки.

const [count, setCount] = useState(0);

useEffect(() => {
  // ESLint предупредит: count не указан в зависимостях
  document.title = `Счётчик: ${count}`;
}, []); // Нужно добавить count

Что хочет услышать интервьюер

Кандидат объясняет три сценария: без массива, с пустым массивом и с зависимостями

Понимание того, что React использует Object.is для сравнения зависимостей

Знание о функции очистки и когда она вызывается

Осознание проблемы со ссылочными типами (объекты, массивы, функции) в зависимостях

Упоминание правила exhaustive-deps или eslint-плагина

Пример: Три варианта dependency array

import { useState, useEffect } from 'react';

function ExampleComponent({ userId }: { userId: number }) {
  const [data, setData] = useState(null);

  // 1. Без массива — запускается после каждого рендера
  useEffect(() => {
    console.log('каждый рендер');
  });

  // 2. Пустой массив — только при монтировании
  useEffect(() => {
    console.log('только при монтировании');
    return () => {
      console.log('при размонтировании');
    };
  }, []);

  // 3. С зависимостью — при монтировании и при изменении userId
  useEffect(() => {
    let cancelled = false;

    async function load() {
      const result = await fetchUser(userId);
      // Проверяем флаг, чтобы избежать обновления после размонтирования
      if (!cancelled) {
        setData(result);
      }
    }

    load();

    return () => {
      // Отменяем устаревший запрос при смене userId
      cancelled = true;
    };
  }, [userId]);

  return <div>{JSON.stringify(data)}</div>;
}

Пример: Проблема со ссылочными зависимостями

import { useState, useEffect, useMemo } from 'react';

function BadExample({ page }: { page: number }) {
  // Объект создаётся заново при каждом рендере — бесконечный цикл!
  const options = { page, limit: 10 };

  useEffect(() => {
    fetchData(options);
  }, [options]); // Всегда новая ссылка на options
}

function GoodExample({ page }: { page: number }) {
  // Стабилизируем объект через useMemo
  const options = useMemo(() => ({ page, limit: 10 }), [page]);

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

Типичные ошибки

Путают пустой массив [] с отсутствием массива и не понимают разницу в поведении

Добавляют объекты или функции в зависимости без стабилизации через useMemo/useCallback, создавая бесконечный цикл рендеров

Намеренно скрывают зависимости в пустом массиве, чтобы запустить эффект только раз, хотя внутри используются изменяемые значения

Не возвращают функцию очистки при подписках и таймерах, что приводит к утечкам памяти

Игнорируют предупреждения eslint-plugin-react-hooks вместо того чтобы разобраться в причине

Лучшие курсы по теме

изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Feature-Sliced Design

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.5
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее