React Strict Mode: зачем он нужен

Подробно разбираем React Strict Mode: какие проверки он включает, почему в dev все «вызывается дважды», какие баги ловит и как безопасно внедрять в production-командах.

06 марта 2026 г.18 минLexicon Team

Введение

React Strict Mode часто воспринимают как «режим, где все ломается и вызывается по два раза». Такая реакция понятна: при первом включении команда видит дубли логов, повторные вызовы эффектов и нестабильные тесты, которые до этого казались «зелеными».

Но Strict Mode не ломает приложение ради эксперимента. Он делает видимыми проблемы, которые уже есть в коде: неидемпотентные эффекты, пропущенный cleanup, зависимости с побочными эффектами, старые API-подходы. В production эти дефекты обычно проявляются позже и дороже: под нагрузкой, в сложной навигации или после большого рефакторинга.

Если вы хотите связать тему с общим жизненным циклом рендера, полезно также ознакомиться с разбор жизненного цикла React-компонента, материал про batching обновлений и объяснение concurrent rendering.

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

Больше вопросов в Telegram

Ежедневные разборы и реальные кейсы с интервью.

Подписаться

Что такое Strict Mode и какую задачу он решает

Короткий ответ

<StrictMode> — это специальный режим React для разработки, который включает дополнительные проверки и помогает находить потенциально опасный код до релиза.

Что он не делает

  • не является фичей «ускорения»;
  • не должен менять production-поведение;
  • не решает архитектуру автоматически.

Что он реально дает команде

  • раннее выявление неочевидных побочных эффектов;
  • проверку корректного cleanup в эффектах;
  • сигнал о рисковых паттернах, плохо совместимых с современным React.

Ключевая мысль: Strict Mode увеличивает количество диагностических сообщений в dev, чтобы уменьшить стоимость ошибок в production.

Архитектура: как Strict Mode встроен в жизненный цикл

В обычном dev-потоке React выполняет рендер, commit и эффекты. В Strict Mode для части сценариев React намеренно добавляет дополнительную проверочную фазу: симулирует повторное выполнение рендера и жизненного цикла эффекта (setup/cleanup/setup).

Упрощенно это выглядит так:

  1. Компонент монтируется.
  2. Эффект выполняет setup.
  3. React выполняет cleanup.
  4. React повторно выполняет setup.

Зачем это нужно:

  • проверить, что эффект корректно очищает ресурсы;
  • убедиться, что setup не содержит необратимых действий;
  • поймать скрытые утечки подписок, таймеров и соединений.

Границы ответственности:

  • Strict Mode выявляет проблемы;
  • разработчик исправляет поток побочных эффектов;
  • тесты фиксируют ожидаемое поведение.

Типичная точка отказа: команда видит повторный сетевой запрос в dev и «чинит» это выключением Strict Mode, вместо того чтобы сделать эффект идемпотентным или вынести триггер в пользовательское действие.

Почему в Strict Mode кажется, что «все вызывается дважды»

Главная причина — специальное поведение в dev-режиме, направленное на обнаружение небезопасных side эффектов. Это не баг React, а инструмент диагностики.

Чаще всего разработчики замечают:

  • двойной лог в теле компонента;
  • повтор setup у useEffect;
  • повторную инициализацию внешнего ресурса без cleanup.

Важно: двойной вызов в dev не равен двойному вызову в production. Но если код в dev ломается, это уже сигнал, что в нем есть хрупкая логика, которая может проявиться и в боевых сценариях (например, при быстрых mount/unmount переходах).

Практический пример: неидемпотентный эффект

function ChatRoom({ roomId }: { roomId: string }) {
  useEffect(() => {
    const connection = createConnection(roomId);
    connection.connect();

    return () => {
      connection.disconnect();
    };
  }, [roomId]);

  return <p>Room: {roomId}</p>;
}

В Strict Mode этот код безопасен: setup повторится, но cleanup между вызовами корректно закроет соединение.

Проблемный вариант:

function ChatRoomBroken({ roomId }: { roomId: string }) {
  useEffect(() => {
    // Подключаемся, но забываем cleanup.
    createConnection(roomId).connect();
  }, [roomId]);

  return <p>Room: {roomId}</p>;
}

Здесь Strict Mode быстро покажет дефект: повторные подключения и утечки. Без него баг может долго скрываться.

Таблица сравнения: Strict Mode и соседние инструменты

КритерийStrict ModeESLint React Hooks RulesUnit/Integration testsRuntime monitoring
Когда работаетDev runtimeСтатический анализВо время тестовНа тестовых окружениях после деплоя
Что ловит лучше всегоНеидемпотентные эффекты, плохой cleanupОшибки зависимостей и паттернов хуковРегрессии сценариевИнциденты и деградации в реальном трафике
Ловит проблемы side effects в динамикеДаЧастичноЧастичноДа
Цена внедренияНизкая/средняяНизкаяСредняяСредняя/высокая
Риск ложных интерпретацийСредний («в dev все дважды»)НизкийСреднийСредний
Заменяет другие инструментыНетНетНетНет

Strict Mode эффективен как часть системы качества, а не как отдельная серебряная пуля.

Production pitfalls: где команды чаще ошибаются со Strict Mode

Ошибка 1. Отключить Strict Mode при первых «дублях»

Симптомы:

  • в репозитории появляется комментарий «иначе дергается два раза»;
  • проблемы cleanup остаются нераскрытыми;
  • баги попадают в production через несколько спринтов.

Последствие: технический долг растет, а диагностика сдвигается вправо.

Профилактика: чинить причину дубля, а не скрывать симптом.

Ошибка 2. Делать сетевой запрос «на mount любой ценой»

Симптомы:

  • запросы стартуют повторно без отмены;
  • race condition при быстрых переходах между экранами;
  • результаты старого запроса перезаписываются более новыми.

Последствие: нестабильный интерфейс, лишняя нагрузка на API.

Профилактика: использовать отмену (AbortController), маркер актуальности запроса и корректный cleanup.

Ошибка 3. Побочные эффекты во время рендера

Симптомы:

  • неожиданные мутации глобального состояния;
  • дубли событий аналитики;
  • сложно воспроизвести порядок вызовов.

Последствие: флейки-тесты и непредсказуемое поведение.

Профилактика: побочные действия только в эффектах/обработчиках событий, а рендер держать чистым.

Ошибка 4. Неучтенный re-run тяжёлой инициализации

Симптомы:

  • резкий рост времени dev-рендера;
  • перегрев local environment на сложных страницах;
  • команда отключает проверки «потому что медленно».

Последствие: ухудшается качество обратной связи в разработке.

Профилактика: выделить тяжелую часть в ленивую инициализацию, мемоизацию по месту и разделение ответственности компонентов.

Разбор производительности

Strict Mode может визуально «замедлить» dev, потому что выполняет больше проверок. Это ожидаемо. Главный вопрос не «почему dev стал тяжелее», а «какие ошибки это помогло найти раньше».

Когда Strict Mode полезен для производительности в долгую:

  • выявляет неочищенные подписки и интервалы;
  • обнаруживает эффекты, вызывающие каскад лишних ререндеров;
  • заставляет код быть устойчивым к повторным mount/unmount сценариям.

Когда он не решит проблему:

  • узкое место в network latency;
  • тяжелая бизнес-функция блокирует main thread;
  • архитектура экрана монолитна и не разделена по ответственности.

Практический подход:

  1. Профилируйте dev-проблемы отдельно от production-метрик.
  2. После фикса эффекта проверяйте рендеры и commit time в React Profiler.
  3. Не делайте выводы о production-FPS только на основании «двойных» вызовов Strict Mode.

Прокачай React за 7 дней

20 вопросов и разборов по React Hooks.

Начать

Когда Strict Mode не покрывает все риски

Strict Mode не проверяет бизнес-валидность данных и не заменяет тестирование end-to-end. Он также не поймает часть проблем, которые проявляются только при реальном трафике, медленной сети и состояниях гонки между клиентом и сервером.

Поэтому минимальный стек контроля качества обычно такой:

  • Strict Mode в dev;
  • линтеры и правила хуков;
  • тесты ключевых сценариев;
  • мониторинг ошибок и пользовательских метрик в production.

Практики, которые работают в командах

  • Включать Strict Mode по умолчанию в новых модулях.
  • Для legacy-кода внедрять поэтапно: экран за экраном, с фиксами cleanup.
  • На code review требовать идемпотентность эффектов и явный teardown ресурсов.
  • Разделять инициализацию ресурса и реакцию на действие пользователя, чтобы избежать смешения разных причин запуска эффектов.
  • Покрывать тестами сценарии быстрой смены props и unmount во время async.
  • Для рискованных рефакторингов использовать feature flag и заранее готовить rollback.

Частые ошибки

  • Считать, что Strict Mode «ломает React».
  • Дублировать analytics-события из-за эффектов без защиты от повторного setup.
  • Хранить внешние экземпляры (подключения) без destroy/disconnect в cleanup.
  • Блокировать main thread тяжелыми вычислениями при каждом mount.
  • Отключать Strict Mode глобально вместо локального исправления проблемного компонента.

Как отвечать на интервью про Strict Mode

Короткий рабочий шаблон:

  1. Strict Mode — dev-инструмент для поиска небезопасных побочных эффектов и устаревших паттернов.
  2. В dev он может повторно запускать рендер/эффектные сценарии, чтобы проверить cleanup и идемпотентность.
  3. На production-сборку эти проверки напрямую не переносятся.
  4. Если Strict Mode выявил дубль запроса, это сигнал к исправлению эффекта, а не к отключению режима.

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

Подготовься к React-собеседованию на реальных кейсах

Разберем Strict Mode, эффекты и рендеринг на практических задачах с обратной связью.

Начать подготовку

Практический кейс из production: защита от дублей запросов в Strict Mode

Сценарий: страница профиля загружает данные пользователя при userId и часто перемонтируется при навигации. В dev Strict Mode видно двойной запуск эффекта, а часть ответов приходит в разном порядке.

Надежный подход:

function UserProfile({ userId }: { userId: string }) {
  const [data, setData] = useState<{ name: string } | null>(null);
  const [loading, setLoading] = useState(false);
  const requestIdRef = useRef(0);

  useEffect(() => {
    const controller = new AbortController();
    const currentRequestId = ++requestIdRef.current;

    setLoading(true);

    fetch(`/api/user/${userId}`, { signal: controller.signal })
      .then((r) => r.json())
      .then((payload) => {
        if (currentRequestId === requestIdRef.current) {
          setData(payload);
        }
      })
      .catch((err: unknown) => {
        if ((err as { name?: string }).name !== "AbortError") {
          if (currentRequestId === requestIdRef.current) {
            setData(null);
          }
        }
      })
      .finally(() => {
        if (currentRequestId === requestIdRef.current) {
          setLoading(false);
        }
      });

    return () => controller.abort();
  }, [userId]);

  if (loading) return <p>Загрузка...</p>;
  if (!data) return <p>Нет данных</p>;
  return <h2>{data.name}</h2>;
}

Почему это работает:

  • cleanup отменяет предыдущий запрос;
  • requestIdRef защищает от устаревших ответов;
  • UI-состояние остается в useState, а технический маркер — в useRef.

Такой паттерн устойчив и в Strict Mode, и в реальных race-сценариях production.

Чек-лист внедрения Strict Mode в существующий проект

  1. Есть ли компоненты с побочными эффектами без cleanup? Если да, начать с них: таймеры, подписки, sockets, observers.

  2. Есть ли сетевые эффекты без отмены/защиты от гонок? Добавить AbortController и маркер актуального запроса.

  3. Есть ли побочные действия во время рендера? Вынести их в эффект или обработчик события.

  4. Есть ли тяжелые mount-инициализации? Разделить ответственность компонента и оптимизировать горячий путь.

  5. Есть ли тесты на быстрый mount/unmount и смену props? Без таких тестов регрессии возвращаются после первого же рефакторинга.

Этот чек-лист помогает включать Strict Mode без хаоса и без массовых «временных» отключений.

FAQ

Strict Mode нужно включать на весь проект сразу?

Не обязательно. В legacy-проектах безопаснее идти поэтапно, начиная с модулей с высоким риском side effects.

Двойной useEffect в dev — это нормально?

Да, для Strict Mode это ожидаемый диагностический сценарий. Важнее проверить, что cleanup корректный.

Можно ли отключить Strict Mode только для части дерева?

Да. Обычно так и делают в миграции: проблемный участок временно изолируют, а затем приводят к корректному поведению.

Strict Mode заменяет тесты?

Нет. Он выявляет отдельный класс ошибок во время разработки, но не покрывает бизнес-сценарии end-to-end.

Почему после включения Strict Mode сломалась аналитика?

Чаще всего события отправляются из неидемпотентного эффекта. Нужно исправить место отправки и добавить защиту от дублей.

Итоги

Strict Mode нужен не для «строгости ради строгости», а для ранней диагностики багов, которые дорого исправлять на поздних этапах. Он поднимает инженерную дисциплину: эффекты становятся чище, cleanup явнее, а код устойчивее к сложным сценариям рендера.

Оптимальная стратегия в командах: не выключать режим из-за шума, а последовательно исправлять причины. Тогда Strict Mode становится не раздражителем, а реальным инструментом снижения production-рисков.

Если хотите углубиться дальше, изучите практику с useEffect и сложными вопросами, разбор React Hooks для интервью и ошибки оптимизации компонентов.

Больше вопросов в Telegram

Ежедневные разборы и реальные кейсы с интервью.

Подписаться

Автор

Lexicon Team

Читайте также