State Management

XState: основы

Идея конечного автомата ясна: набор состояний и разрешённые переходы. Но как выразить её в коде так, чтобы переходы соблюдались, а не оставались договорённостью в комментариях? Можно вручную писать switch по состоянию и событию, но он расползается и не отлавливает забытые случаи. XState задаёт вопрос: а что, если описать состояния, события и переходы как данные в createMachine, а библиотека сама гарантирует, что переход вне схемы не случится, и нарисует диаграмму по этому описанию.

  • Загрузка данных как машина idle, loading, success, error с переходами по событиям
  • Многошаговые формы и мастера: состояние шага и условия перехода в createMachine
  • Светофоры UI и тогглы со сложной логикой: модалки, тулбары, режимы редактора
  • Платежи и заказы, где набор статусов и переходов критичен и проверяется визуализатором
  • Команды, рисующие машину в Stately-визуализаторе до написания кода компонента

Предварительные знания

  • Идея конечного автомата: состояния, события и разрешённые переходы
  • Понимание, почему невозможные состояния опасны и как их исключают
  • Базовая работа с хуками React и подпиской компонента на изменения
  • Машины состояний: зачем

Откуда взялся XState

XState создал Дэвид Куршид в 2017 году, чтобы принести statecharts Дэвида Харела в мир JavaScript. Библиотека описывает машину как объект-конфигурацию: состояния, события, переходы, контекст. К версии XState v5, вышедшей в 2024 году, модель сместилась к акторам: машина это актор, который получает события, держит состояние и может порождать другие акторы. Параллельно команда сделала визуальный редактор Stately, где машину можно нарисовать мышью и получить код, или наоборот построить диаграмму по существующей конфигурации. Так описание состояний и его картинка стали одним и тем же артефактом.

createMachine: состояния, события, переходы

Машина в XState описывается функцией createMachine как объект-конфигурация. В нём задают id, начальное состояние initial и словарь states. У каждого состояния есть блок on, где ключи это имена событий, а значения это целевые состояния. Это прямое выражение таблицы переходов: из какого состояния по какому событию в какое.

Эта конфигурация и есть схема из предыдущего урока, но теперь исполняемая. Из idle событие FETCH ведёт в loading. Из loading RESOLVE ведёт в success, REJECT в error. Событие, которого нет в блоке on текущего состояния, игнорируется: переход вне схемы просто не происходит. Гарантия разрешённых переходов теперь не в комментарии, а в данных машины.

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

Что описывает блок on у состояния в конфигурации createMachine?

context и расширенное состояние

Конечных состояний мало, чтобы хранить всё. Состояние loading не говорит, сколько было попыток, а success не несёт сами данные. Для таких расширенных данных у машины есть context: объект произвольных полей, который живёт рядом с конечным состоянием. Конечное состояние отвечает на вопрос где мы, а context на вопрос с какими данными.

context меняется не напрямую, а через action assign в описании перехода. По событию INC выполняется assign, который пересчитывает count из текущего контекста. Это держит изменения данных внутри схемы машины: данные меняет только переход, и только так, как описано. Здесь переход не меняет конечное состояние, а лишь обновляет context, что тоже допустимо.

  • Конечное состояние — Один из дискретного набора: idle, loading, success. Отвечает на вопрос в каком режиме система
  • context — Объект с данными: счётчики, ответы, ошибки. Отвечает на вопрос с какими значениями, меняется через assign

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

Зачем машине нужен context помимо конечных состояний?

useMachine: машина в React

Чтобы использовать машину в компоненте, в XState v5 есть хук useMachine из пакета @xstate/react. Он принимает машину и возвращает кортеж: текущий snapshot состояния и функцию send для отправки событий. Компонент читает state, чтобы понять текущее состояние и context, и вызывает send, чтобы инициировать переходы.

Текущее состояние проверяют методом state.matches, который возвращает признак нахождения в нужном состоянии. События шлют через send объектом с полем type. Важно, что send не присваивает состояние напрямую: он передаёт событие машине, а уже та решает, разрешён ли переход. Отправка события, для которого в текущем состоянии нет перехода, безопасно игнорируется.

В XState v5 машина под капотом это актор. useMachine создаёт экземпляр актора, привязанный к жизни компонента, подписывается на его snapshot и перерисовывает компонент при смене состояния или context. Через send компонент общается с актором событиями, а не дёргает поля напрямую. Это та же модель сообщений, что лежит в основе акторного подхода XState v5.

Так замыкается цепочка из группы: конечный набор состояний и переходы из предыдущего урока становятся исполняемой конфигурацией в createMachine, а useMachine подключает её к UI. Переход вне схемы не происходит ни в логике, ни в компоненте, и невозможные состояния остаются невыразимыми уже в рантайме.

Что возвращает хук useMachine и как компонент инициирует переход?

Связь с другими темами

Этот урок про реализацию автоматов в XState. Рядом стоят соседи:

  • Машины состояний: зачем — XState это конкретная реализация идей конечного автомата из предыдущего урока
  • Statecharts: иерархия и параллелизм — На createMachine дальше строятся вложенные и параллельные состояния

Итог

  • createMachine описывает машину как конфигурацию: states, события, переходы и начальное состояние
  • Состояние меняется только событием: в блоке on состояния заданы переходы по именам событий
  • context хранит расширенные данные машины (счётчики, ответы), которые меняются через assign в actions
  • В XState v5 машина это актор: он получает события через send и отдаёт текущее состояние
  • useMachine из @xstate/react подключает машину к компоненту: отдаёт state и функцию send
  • Переход вне схемы не происходит, и это та же гарантия невозможных состояний, но уже в рантайме

Связанные уроки

  • sm-25-state-machines-intro — XState это реализация конечных автоматов, разобранных в предыдущем уроке
  • sm-27-statecharts — На основе createMachine дальше строятся иерархические и параллельные statecharts
XState: основы

0

1

Войти