Перейти к содержимому
Все статьи
Разработка

UseEffect срабатывает дважды: проверяем StrictMode и очистку

·27 октября 2026 г.· 6 мин чтения
Сколько будет стоить ваш проект?Рассчитать стоимость

Если useEffect срабатывает дважды, сначала проверьте среду, область StrictMode и последовательность setup/cleanup. В разработке StrictMode может выполнить дополнительный проверочный цикл эффекта. Но повтор также бывает из-за изменения зависимостей или нового монтирования: отключение режима не устанавливает причину и не устраняет утечку подписки.

Сначала уточните, что именно повторилось

Сообщение в теле компонента показывает выполнение рендера, а сообщение внутри эффекта — другую часть жизненного цикла. Два сетевых обращения тоже не всегда означают два запуска одного эффекта: запрос может повторять библиотека или другой участок приложения. Разделите эти наблюдения до исправления.

Временный диагностический журнал должен различать экземпляр компонента, запуск эффекта и освобождение созданного ресурса. Добавьте значения, от которых зависит подключение. Тогда последовательность расскажет больше, чем одинаковые сообщения «запустилось», по которым невозможно понять источник повторения.

Что проверяет StrictMode

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

В справке useEffect описан дополнительный setup/cleanup перед обычным setup в режиме разработки. Его смысл — обнаружить неполную очистку. Наблюдаемый результат для пользователя должен оставаться корректным, когда соединение создаётся, освобождается и создаётся снова.

Это не обещание, что production никогда не повторит эффект. Изменение зависимостей и новое монтирование остаются возможными событиями приложения. Если проблема видна в рабочей сборке, не останавливайтесь на объяснении «это просто StrictMode»: проверьте фактическую последовательность.

Учебный журнал подписки без утечки

Представим эффект, который подписывается на обновления выбранной темы и в cleanup снимает именно созданную подписку. Названия действий условны, это не API конкретной библиотеки. В таблице число активных подписок — ожидаемый результат учебного сценария, а не выполненный здесь браузерный тест.

Шаг учебной последовательностиДействие эффектаЧто освобождаетсяОжидаемо активных подписокЧто означал бы другой результат
Первый setupСоздать подписку С-1 на тему АПока ничего1Ноль: подключение не создано или не подтверждено
Проверочный cleanupОстановить С-1Та же подписка С-10Одна: очистка не сняла созданный ресурс
Следующий setupСоздать С-2 на тему АС-1 уже остановлена1Две: прежний ресурс продолжает жить
Тема изменилась на БCleanup С-2, затем setup С-3Прежняя подписка темы А1Ответы темы А продолжают менять экран Б
Компонент удалёнCleanup С-3Последняя действующая подписка0Остаётся обработчик без нужного экрана

При неисправной очистке второй setup лишь делает проблему заметнее. Однократный запуск тоже оставит подписку после ухода пользователя, если cleanup не освобождает её. Исправление принимают по судьбе ресурса, а не по исчезновению второго сообщения в консоли.

Почему cleanup вызывается без удаления компонента

Справка useEffect объясняет, что очистка выполняется не только при удалении компонента, но и перед новым setup при изменённых зависимостях. Это позволяет отключить прежнюю синхронизацию до подключения к новому состоянию. Удалять такую очистку потому, что экран ещё виден, неправильно.

Проверьте, что очистка использует ресурс своего запуска. Если она обращается к изменяемой общей переменной, там уже может оказаться другая подписка. Для обсуждения с разработчиком полезно подписывать создание и остановку одним идентификатором, не записывая в журнал секретные данные.

Когда причина — зависимости, а когда — новое монтирование

Посмотрите, что изменилось между запусками. Объект или функция, создаваемые заново при рендере, могут влиять на повтор синхронизации, если участвуют в зависимостях. Но удаление зависимостей наугад способно оставить эффект с устаревшими значениями. Сначала определите, с чем он должен синхронизироваться.

Если компонент удаляется и создаётся вновь из-за условий интерфейса или изменения ключа, это другая история, чем обновление существующего экземпляра. В журнале должны отличаться эти случаи. Не пытайтесь решить оба одним флагом, который просто запрещает повторное выполнение.

Пустой список зависимостей не означает вечную гарантию одного запуска за жизнь приложения. Компонент может появиться заново, а режим разработки — проверить его работу. Требование следует формулировать через допустимое создание и освобождение ресурсов, а не через желаемое число сообщений.

Почему флаг «уже выполнялось» скрывает проблему

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

Отдельно рассмотрите необратимые действия. В руководстве React о синхронизации эффектами различаются эффекты отображения и действия, вызванные конкретным пользовательским событием. Команда вроде подтверждения покупки не должна выполняться только потому, что экран появился.

Если запрос уже ушёл, локальная отмена ожидания не доказывает отмену серверного действия. Для команд с последствиями нужны собственные правила обработки и защиты на сервере. Не используйте StrictMode как объяснение, почему допустимо повторно выполнить такую команду.

Проверка исправления в правильном порядке

  1. Разделите логи рендера, setup, cleanup и фактических запросов.
  2. Проверьте режим сборки и место включения StrictMode.
  3. Зафиксируйте зависимости и идентичность экземпляра между повторами.
  4. Убедитесь, что каждый cleanup освобождает ресурс соответствующего setup.
  5. Проверьте смену темы, уход со страницы и повторное появление компонента.
  6. Сравните поведение разработки и рабочей сборки, сохранив проверку корректности результата.

Не принимайте исправление только по одному открытию экрана. Для подписки нужен сценарий смены источника; для таймера — отсутствие лишнего работающего экземпляра после ухода. Для запроса важна судьба ответа, если экран уже изменился. Это разные ресурсы и разные признаки приёмки.

Что включить в задачу на доработку

В запрос на доработку сайта приложите небольшой воспроизводимый маршрут и последовательность событий. Для веб-приложения опишите ожидаемые подключение, переключение и освобождение ресурса. Большой снимок консоли без контекста хуже короткого журнала одного сценария.

Если эффект связан с API-интеграцией, отдельно определите, где чтение состояния, а где команда. После исправления полезная проверка разработки остаётся, а повторный setup не создаёт лишнего ресурса и не повторяет значимое действие пользователя.

Вопросы и ответы

Почему useEffect вызывается два раза в React?

В режиме разработки причиной может быть дополнительный цикл проверки StrictMode. Но нужно также проверить зависимости и новое монтирование: одинаковое число запусков не доказывает одинаковую причину.

Когда выполняется cleanup у useEffect?

При удалении компонента и перед новым setup, если изменились зависимости; в соответствующей настройке StrictMode есть также проверочный цикл разработки. Очистка должна остановить или освободить то, что создал её запуск эффекта.

Поможет ли пустой массив зависимостей избежать повторов?

Он не отменяет новое монтирование и проверочное поведение режима разработки. Не удаляйте нужные зависимости ради тишины в журнале: сначала определите корректные условия синхронизации и очистки.

Нужна помощь с проектом?

Обсудим задачу и предложим решение — от сайта до SaaS и защиты.

Связаться