State Management
Машины: когда уместны
Команда добавляет XState в проект, потому что прочитала статью про машины состояний, и оборачивает в машину обычный переключатель темы: два состояния, один переход, пятнадцать строк конфигурации там, где хватило бы useState с булевым флагом. Через месяц в том же проекте экран оплаты живёт на трёх useState и каскаде if-else, где половина переходов невозможна, но код их допускает - и именно там машина была бы спасением. Инструмент перепутали местами с задачей. Машина состояний это не галочка зрелости, а ответ на конкретный вопрос: сколько у потока правил и насколько больно их нарушить.
- Stripe Checkout: оплата проходит через множество состояний (ввод, 3D Secure, обработка, успех, отказ, ретрай), где запрещённые переходы стоят денег
- Онбординг-визарды в Notion и Linear: шаги с условиями, ветвлениями и возможностью вернуться назад - классический кейс для машины
- Переподключение websocket в Slack и Figma: connecting, connected, reconnecting с backoff, failed - поток с ретраями и таймерами
- Видеоплееры (YouTube, Twitch): idle, buffering, playing, paused, ended плюс переходы между ними по строгим правилам
- Простые тогглы, аккордеоны, дропдауны: один булев флаг, где машина только добавляет церемонии без выгоды
Предварительные знания
- Машина состояний: что такое состояния, события и переходы
- Опыт с useState и булевыми флагами для простого UI-состояния
- Понимание, что такое невозможное состояние (например, isLoading и isError одновременно true)
Где машина состояний выигрывает
Машина состояний окупается, когда у потока много состояний и переходы между ними подчинены правилам. Главная выгода - запрет невозможных состояний. В коде на булевых флагах ничто не мешает выставить isLoading и isError одновременно в true, хотя по смыслу это бессмыслица. Машина делает такую комбинацию недостижимой: актор в каждый момент находится ровно в одном состоянии, а переход разрешён только если он явно описан.
- Оплата: ввод данных, 3D Secure, обработка, успех, отказ, повторная попытка - переходы строгие, цена ошибки прямая
- Онбординг-визард: шаги с ветвлением, возврат назад, валидация перед переходом дальше
- Переподключение websocket: connecting, connected, reconnecting с экспоненциальным backoff, failed после N попыток
- Медиаплеер: idle, buffering, playing, paused, ended с правилами, какой переход откуда возможен
Общее у всех этих кейсов: переходов больше горстки, у них есть условия, и неверный переход (оплатить дважды, проиграть до загрузки) приводит к реальному вреду. Машина превращает эти правила из комментариев в коде в саму структуру, которую невозможно нарушить.
В чём главная практическая выгода машины состояний для потока оплаты?
Где машина это перебор
Обратная сторона: если у состояния два значения и один переход между ними, машина только добавляет церемонии. Переключатель темы, открытие дропдауна, раскрытие аккордеона - это булев флаг. Завернуть его в createMachine значит написать пятнадцать строк конфигурации, импортов и типов там, где useState решает задачу одной строкой, и при этом ничего не выиграть: невозможных состояний тут нет, ломать нечего.
- Машина оправдана — Состояний больше горстки, переходы условны, неверный переход вредит. Оплата, онбординг, реконнект, плеер. Правила переходов стоит зафиксировать в структуре.
- Машина это перебор — Два-три значения, один-два перехода без условий, цена ошибки нулевая. Тоггл, дропдаун, таб. useState или useReducer закрывают вопрос.
Распространённая ошибка - тащить XState в проект как признак зрелости и оборачивать в машину всё подряд. Перебор стоит реально: лишний вес бандла, выше порог входа для команды и больше кода на ровном месте. Машина должна заслужить своё присутствие сложностью потока.
Для какого из случаев машина состояний скорее всего избыточна?
Как принять решение
Решение сводится к нескольким наблюдаемым сигналам. Если в компоненте накапливается каскад булевых флагов и появляются проверки вида 'если loading и при этом не error и при этом не success' - это запах невозможных состояний, которые лучше описать машиной. Если в потоке есть переходы, которые код допускает, но логика запрещает (вернуться к оплате после успеха), машина закроет эту дыру структурно.
| Сигнал | Что значит | Решение |
|---|---|---|
| Один булев флаг, один переход | Простое UI-состояние | useState |
| Несколько флагов, простые комбинации | Переходов мало, правил почти нет | useReducer |
| Каскад флагов, проверки 'loading и не error' | Невозможные состояния достижимы | Машина состояний |
| Запрещённые переходы, ретраи, таймеры | Сложный поток с правилами | Машина состояний |
Промежуточный вариант - useReducer. Он даёт явные переходы через действия без веса и кривой обучения XState. Если переходов немного и невозможных состояний нет, reducer часто закрывает потребность, не доводя до полноценной машины.
Какой признак в коде сильнее всего намекает, что пора заменить набор useState машиной состояний?
Связь с другими темами
Урок про критерий выбора. Дальше тема расходится так:
- Введение в машины состояний — База, на которой строится разговор: без понятия переходов нет и разговора об их сложности
- XState: акторы — Когда поток сложный, его часто дробят на акторов, и тогда машина оправдана вдвойне
Итог
- Машина состояний оправдана, когда переходов много, у них есть правила и нарушение правила стоит дорого (оплата, онбординг, websocket с ретраями)
- Главная выгода - невозможные состояния становятся непредставимыми: машина не даёт уйти в комбинацию, которой не существует по логике потока
- Сигналы 'пора брать машину': каскад булевых флагов, проверки вида 'если loading и при этом error', запрещённые переходы, которые код всё равно допускает
- Простой тоггл, дропдаун, аккордеон обходятся одним useState. Машина тут добавляет конфигурацию и церемонию без выгоды
- Критерий - сложность потока и цена ошибки, а не мода на машины состояний
Связанные уроки
- sm-25-state-machines-intro — Чтобы рассуждать о границе применимости, нужно базовое понятие машины состояний и её переходов
- sm-28-xstate-actors — Сложные потоки часто разбиваются на акторов, поэтому критерий уместности машины и тема акторов идут рядом