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

Как будет работать useEffect, если массива зависимостей нет?

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

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

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

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

Как будет работать useEffect, если массива зависимостей нет

Если у useEffect нет массива зависимостей, эффект будет запускаться ПОСЛЕ КАЖДОГО рендера компонента. Это критически важная особенность, которая часто приводит к багам и бесконечным циклам.

Три варианта useEffect

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

// 2. С ПУСТЫМ массивом — запускается только ОДИН РАЗ при монтировании
useEffect(() => {
  console.log('Запускается только при монтировании');
}, []);

// 3. С ЗАВИСИМОСТЯМИ — запускается, когда зависимости меняются
useEffect(() => {
  console.log('Запускается когда value меняется');
}, [value]);

Пример: useEffect БЕЗ массива зависимостей

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

  // БЕЗ массива зависимостей
  useEffect(() => {
    console.log('Запрос данных пользователя');
    fetch(`/api/users/${userId}`)
      .then(res => res.json())
      .then(data => setUser(data));
  }); // <- НЕТУ массива зависимостей!

  return <div>{user?.name}</div>;
}

Что произойдёт:

1. Компонент монтируется
2. useEffect запускается -> fetch запрос
3. Получаем данные -> setUser(data)
4. Компонент ре-рендерится
5. useEffect запускается СНОВА -> ещё один fetch запрос
6. Получаем данные -> setUser(data)
7. Компонент ре-рендерится
8. Шаг 5-7 повторяются БЕСКОНЕЧНО!

Результат: бесконечный цикл запросов!

Визуализация жизненного цикла

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

  // БЕЗ массива зависимостей
  useEffect(() => {
    console.log('Effect запустился. Count = ' + count);
  });

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
    </div>
  );
}

Вывод в консоли:

Effect запустился. Count = 0       (первый рендер)
Effect запустился. Count = 1       (клик на кнопку)
Effect запустился. Count = 2       (клик на кнопку)
Effect запустился. Count = 3       (клик на кнопку)
...

После каждого клика на кнопку effect запускается!

Правильные варианты

1. С пустым массивом зависимостей (mount/unmount)

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

  // С ПУСТЫМ массивом — запускается ОД ОДИН РАЗ
  useEffect(() => {
    console.log('Component mounted');
    fetch(`/api/users/${userId}`)
      .then(res => res.json())
      .then(data => setUser(data));
    
    return () => {
      console.log('Component unmounted');
    };
  }, []); // <- пустой массив!

  return <div>{user?.name}</div>;
}

// Вывод:
// Component mounted (один раз)
// fetch запрос сделается один раз

2. С зависимостями (когда зависимость меняется)

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

  // С ЗАВИСИМОСТЯМИ — запускается, когда userId меняется
  useEffect(() => {
    console.log(`Загружаю пользователя ${userId}`);
    fetch(`/api/users/${userId}`)
      .then(res => res.json())
      .then(data => setUser(data));
  }, [userId]); // <- userId это зависимость!

  return <div>{user?.name}</div>;
}

// Вывод:
// Загружаю пользователя 1 (userId=1 впервые)
// Загружаю пользователя 2 (userId изменился на 2)
// Загружаю пользователя 3 (userId изменился на 3)

Когда useEffect БЕЗ зависимостей может быть полезен

Очень редко! Но есть случаи:

1. Отслеживание всех рендеров для отладки

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

  useEffect(() => {
    console.log('Component rendered. Time:', new Date().toLocaleTimeString());
  }); // Без зависимостей — логируем каждый рендер

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Render me</button>
    </div>
  );
}

2. Отправка аналитики

function AnalyticsComponent() {
  useEffect(() => {
    // Отправляем событие при каждом рендере
    analytics.trackPageView({
      timestamp: Date.now(),
      pathname: window.location.pathname
    });
  }); // Без зависимостей — отправляем при каждом рендере
}

НО: в 99% случаев это ошибка!

Типичная ошибка с бесконечным циклом

// ПЛОХО: бесконечный цикл
function BadComponent() {
  const [data, setData] = useState([]);

  useEffect(() => {
    fetch('/api/data')
      .then(res => res.json())
      .then(newData => setData(newData)); // Вызывает рендер
  }); // <- БЕЗ зависимостей!

  // Результат:
  // 1. Рендер -> useEffect
  // 2. setData -> рендер
  // 3. useEffect запускается СНОВА
  // 4. setData -> рендер
  // ... БЕСКОНЕЧНО
}

// ХОРОШО: зависимости
function GoodComponent() {
  const [data, setData] = useState([]);

  useEffect(() => {
    fetch('/api/data')
      .then(res => res.json())
      .then(newData => setData(newData));
  }, []); // <- пустой массив, запуск один раз
}

Чистка сайд-эффектов

function SubscriptionComponent({ userId }) {
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    // Подписываемся на сообщения
    const unsubscribe = subscribeToMessages(userId, (msg) => {
      setMessages(prev => [...prev, msg]);
    });

    // Cleanup функция
    return () => {
      console.log('Cleaning up subscription');
      unsubscribe(); // Отписываемся
    };
  }, [userId]); // Переподписываемся, когда userId меняется

  return <div>{messages.length} messages</div>;
}

// Вывод:
// userId=1: Подписались
// userId=2: Cleaning up subscription + Подписались снова
// userId=3: Cleaning up subscription + Подписались снова

React Strict Mode и двойной эффект

function StrictModeComponent() {
  useEffect(() => {
    console.log('Effect');
    return () => console.log('Cleanup');
  }, []);

  return <div>Hello</div>;
}

// В React Strict Mode (development):
// Вывод:
// Effect
// Cleanup
// Effect

// Это нормально! React проверяет, что cleanup работает корректно
// В production будет только один Effect и один Cleanup

Как избежать ошибок

1. ESLint plugin for React Hooks

npm install --save-dev eslint-plugin-react-hooks
// .eslintrc.json
{
  "extends": ["plugin:react-hooks/recommended"]
}

Плагин предупредит:

WARNING: Missing dependency 'userId' in useEffect hook

2. Правило: если используешь переменную в useEffect — добавь в зависимости

// ПЛОХО
function Component({ userId }) {
  useEffect(() => {
    console.log(userId); // используем userId
  }, []); // но не добавили в зависимости!
}

// ХОРОШО
function Component({ userId }) {
  useEffect(() => {
    console.log(userId);
  }, [userId]); // добавили в зависимости
}

Резюме

useEffect БЕЗ массива зависимостей:

  • Запускается ПОСЛЕ КАЖДОГО рендера
  • В 99% случаев это ошибка и приводит к бесконечным циклам
  • Используется только в редких случаях (отладка, аналитика)

Правильное использование:

  • [] (пустой массив) — запуск один раз при монтировании
  • [dependency] — запуск, когда dependency меняется
  • Всегда добавляй используемые переменные в массив зависимостей

Инструменты:

  • eslint-plugin-react-hooks — поможет избежать ошибок
  • React DevTools Profiler — видеть, когда запускаются эффекты
Как будет работать useEffect, если массива зависимостей нет? | PrepBro