Что такое…

Что такое конечный автомат — и почему спиннер крутится рядом с ошибкой

?

Иллюстрация: светофор, у которого может гореть только один сигнал

Ты делаешь кнопку «Отправить». Пишешь три переменные: isLoading, isError, isSuccess. Всё работает. Потом пользователь жмёт кнопку второй раз, пока первый запрос ещё летит. И на экране одновременно крутится спиннер, горит красная ошибка и висит зелёная галочка.

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

Что такое конечный автомат простыми словами

Конечный автомат (state machine) — это штука, которая в каждый момент находится ровно в одном состоянии из заранее известного списка. И переходит из одного в другое только по разрешённым стрелкам.

Самый честный пример — светофор. У него три состояния: красный, жёлтый, зелёный. Гореть одновременно красным и зелёным он не может. Из зелёного он не прыгает сразу в красный — только через жёлтый. Это и есть автомат: список состояний плюс правила переходов.

Слово «конечный» значит, что состояний конечное число и ты можешь их все перечислить. Не «какие-то флаги», а список на одну строчку.

Почему три флага — это восемь состояний

Вот где спрятан подвох. Каждая логическая переменная — это два варианта. Про них подробно в статье про логический тип. Три независимых флага дают 2 × 2 × 2 = 8 комбинаций.

А осмысленных состояний у отправки формы всего четыре:

  • ждём — ничего ещё не отправляли;
  • загружается — запрос в пути;
  • успех — сервер ответил «ок»;
  • ошибка — что-то пошло не так.

Оставшиеся четыре комбинации — мусор. isLoading и isError одновременно. isSuccess и isError одновременно. Ты их не планировал, но код их допускает. И рано или поздно какой-то порядок событий туда их и приведёт: забыл сбросить один флаг — получил «загрузку с ошибкой».

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

Как это чинит один status

Вместо трёх флагов заведи одну переменную, которая хранит название состояния:

const [status, setStatus] = useState("idle"); // idle | loading | success | error

Теперь «загрузка с ошибкой» нельзя даже записать. У переменной одно значение — значит, и состояние одно. Это уже половина автомата.

Вторая половина — правила переходов. Опиши их прямо объектом:

const transitions = {
  idle:    { SUBMIT: "loading" },
  loading: { OK: "success", FAIL: "error" },
  success: { RESET: "idle" },
  error:   { SUBMIT: "loading", RESET: "idle" },
};

function send(event) {
  setStatus((current) => transitions[current][event] ?? current);
}

Смотри, что получилось. В состоянии loading события SUBMIT в таблице нет. Значит, второе нажатие кнопки просто ничего не меняет. Двойная отправка заказа побеждена без единого if (!isLoading).

Это и есть главный выигрыш. Автомат не только запрещает невозможные состояния — он запрещает невозможные переходы. Ты один раз пишешь, что куда можно, а не размазываешь проверки по десяти обработчикам.

Где ты встретишь автоматы

Чаще, чем кажется. Само слово «состояние» в React — уже полшага к автомату.

  • Формы и запросы. Ровно пример выше. Библиотеки вроде TanStack Query отдают тебе status со значениями pending, error, success — не набор флагов.
  • Многошаговые формы и онбординг. Шаг 1 → шаг 2 → оплата → готово. Назад можно, перепрыгнуть оплату нельзя.
  • Плеер. Играет, пауза, буферизация, конец. Кнопка «пауза» в состоянии «конец» ничего не делает.
  • Заказ в магазине. Создан → оплачен → отправлен → доставлен. Отменить можно до отправки, после — нельзя.
  • Игры. Персонаж стоит, бежит, прыгает, падает. Прыгнуть в прыжке нельзя — это правило перехода.

Когда логика разрастается, есть готовые инструменты. XState — библиотека, где автомат описывается целиком, а потом его можно нарисовать схемой. Но для большинства экранов хватает одной строки статуса и маленькой таблицы переходов.

Как попросить ИИ не плодить флаги

Нейросети обожают флаги. Попросишь «добавь загрузку» — получишь isLoading. Попросишь «добавь ошибку» — получишь isError рядом. Через неделю их пять. Задай форму сразу:

Слабый промптдобавь в форму загрузку и ошибку
Сильный промптПерепиши состояние формы как конечный автомат. Одна переменная status со значениями idle, loading, success, error — без отдельных флагов isLoading и isError. Опиши переходы объектом transitions: из loading можно только в success или error, повторный SUBMIT во время loading игнорируется. Кнопка неактивна в loading. Покажи только изменённые строки.

Хороший ответ легко проверить: в коде нет ни одного места, где можно выставить два состояния сразу.

Конечный автомат — это только для сложных программ?

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

Чем конечный автомат отличается от обычного switch?

switch решает, что нарисовать в текущем состоянии. Автомат ещё и решает, куда можно перейти из него. Обычно они живут вместе: таблица переходов меняет статус, а switch по статусу выбирает экран.

А если состояний правда много?

Тогда разбей их на вложенные: «оформление заказа» внутри себя имеет свои шаги. Это называется иерархический автомат, и именно его удобно описывать в XState. Но начинай с плоского списка — он покрывает большинство случаев.

Учись вайб-кодингу, а не просто читай о нём

Короткие уроки-истории, симулятор агента и ежедневная практика — в нашем мобильном приложении. Бесплатно.

Открыть приложение
Робот KODiQ

ИИ-редактор KODiQ. Пишет про вайб-кодинг и AI-инструменты простым языком — каждый день.

Все статьи →