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

Когда будет добавляться новый слушатель в useEffect?

2.0 Middle🔥 281 комментариев
#JavaScript Core

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

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

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

Время добавления нового слушателя в useEffect

Новый слушатель (или событие, эффект) в React's useEffect добавляется после завершения рендеринга компонента и перед тем, как обновления будут применены к DOM. Это ключевой момент жизненного цикла функциональных компонентов React с хуками.

Точный момент добавления

Реакт выполняет хуки эффектов после того, как браузер отрисует изменения на экране. Это гарантирует, что эффекты (включая добавление слушателей событий) не блокируют процесс отрисовки и не приводят к визуальным несоответствиям. Процесс выглядит так:

  1. Рендеринг компонента: Реакт вычисляет новое состояние и генерирует виртуальный DOM.
  2. Сравнение с DOM: Реакт сравнивает изменения с реальным DOM.
  3. Коммит изменений: Реакт применяет минимально необходимые изменения к реальному DOM.
  4. Вызов useEffect: После коммита изменений в DOM, Реакт вызывает функции, переданные в useEffect.
import { useEffect } from 'react';

function MyComponent() {
  useEffect(() => {
    // Этот код выполнится ПОСЛЕ того, как компонент отрендерится и DOM обновится
    const handleClick = () => console.log('Clicked!');
    document.addEventListener('click', handleClick);

    return () => {
      // Функция очистки удалит слушатель перед следующим эффектом или unmount
      document.removeEventListener('click', handleClick);
    };
  }, []); // Пустой массив зависимостей означает, что эффект выполнится только после первого рендера

  return <div>My Component</div>;
}

Ключевые детали по зависимостям

Момент повторного добавления слушателя напрямую зависит от массива зависимостей второго аргумента useEffect:

  • Пустой массив []: Эффект (и слушатель) добавится один раз после первого рендера. Это аналог componentDidMount в классах.
  • Массив с зависимостьями [dep1, dep2]: Эффект (и слушатель) будет повторно добавляться после каждого рендера, в котором хотя бы одна из зависимостей изменила своё значение. Старый слушатель будет удалён через функцию очистки перед добавлением нового.
useEffect(() => {
  // Этот слушатель добавится после первого рендера,
  // и повторно после любого рендера, где изменится `someState`
  const listener = () => doSomething();
  window.addEventListener('resize', listener);

  return () => window.removeEventListener('resize', listener);
}, [someState]); // Массив зависимостей контролирует повторное выполнение
  • Отсутствие массива зависимостей: Эффект будет выполняться после каждого рендера компонента. Это может привести к частому повторному добавлению/удалению слушателей и обычно требует оптимизации.

Почему это важно для слушателей событий

Такое поведение критично для корректной работы с DOM событиями и внешними API:

  • Гарантия существования DOM элемента: Мы уверены, что DOM-элемент, на который мы хотим добавить слушатель, уже существует в документе.
  • Предотвращение утечек памяти: Функция очистки (возвращаемая из эффекта) удаляет старый слушатель перед добавлением нового или перед unmount компонента. Без этого слушатели оставались бы в памяти, вызывая ошибки и утечки.
  • Синхронизация с состоянием: Если слушатель зависит от значения состояния (например, использует текущий count), его повторное добавление при изменении этого состояния гарантирует, что слушатель работает с актуальными данными.

Пример с полным циклом

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

  useEffect(() => {
    // Слушатель добавится:
    // 1) После первого рендера (массив зависимостей [userId])
    // 2) После любого рендера, где `userId` изменится
    const fetchUserData = async () => {
      const res = await fetch(`/api/user/${userId}`);
      setData(await res.json());
    };

    // Добавляем слушатель на гипотетическое событие
    eventBus.on('userUpdateNeeded', fetchUserData);

    // ОЧИСТКА: перед следующим выполнением этого эффекта (из-за нового userId)
    // или перед unmount компонента этот слушатель будет удалён
    return () => {
      eventBus.off('userUpdateNeeded', fetchUserData);
    };
  }, [userId]); // Зависимость от пропса userId

  return <div>{data ? data.name : 'Loading...'}</div>;
}

Итог: Новый слушатель в useEffect всегда добавляется после рендера и обновления DOM, а точный момент (первый рендер или повторный) контролируется массивом зависимостей. Функция очистки обеспечивает удаление предыдущего слушателя, что является обязательной практикой для предотвращения ошибок и утечек памяти в React приложениях.