Конечные состояния
Конечное состояние — это одно из возможных состояний, в котором машина состояний может находиться в любой момент времени. Оно называется «конечным», потому что машины состояний имеют известное ограниченное количество возможных состояний. Состояние представляет, как машина «ведёт себя» в этом состоянии; её статус или режим.
Например, в форме обратной связи вы можете находиться в состоянии заполнения формы или в состоянии отправки формы. Вы не можете одновременно заполнять форму и отправлять её; это «невозможное состояние».
Машины состояний всегда начинают с начального состояния и могут заканчиваться в финальном состоянии. Машина состояний всегда находится в конечном состоянии.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | |
Вы можете комбинировать конечные состояния с контекстом, которые составляют общее состояние машины:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | |
Начальное состояние¶
Начальное состояние — это состояние, в котором машина начинает работу. Оно определяется свойством initial в конфигурации машины:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
Подробнее о начальных состояниях.
Узлы состояний¶
В XState узел состояния — это «узлы» конечных состояний, которые составляют всё дерево диаграммы состояний. Узлы состояний определяются в свойстве states других узлов состояний, включая корневую конфигурацию машины (которая сама является узлом состояния):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | |
Теги¶
Узлы состояний могут иметь теги — строковые термины, которые помогают группировать или категоризировать узел состояния. Например, вы можете указать, какие узлы состояний представляют состояния, в которых загружаются данные, используя тег "loading", и определить, содержит ли состояние эти помеченные узлы состояний с помощью state.hasTag(tag):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | |
Подробнее о тегах.
Метаданные¶
Метаданные — это статические данные, которые описывают соответствующие свойства узла состояния. Вы можете указать метаданные в свойстве .meta любого узла состояния. Это может быть полезно для отображения информации о узле состояния в UI или для генерации документации.
Свойство state.meta собирает данные .meta из всех активных узлов состояний и помещает их в объект с ID узла состояния в качестве ключа и данными .meta в качестве значения:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | |
Переходы¶
Переходы — это способ перехода от одного конечного состояния к другому. Они определяются свойством on на узле состояния:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | |
Подробнее о событиях и переходах.
Цели¶
Свойство target перехода определяет, куда машина должна перейти, когда переход выполняется. Обычно оно нацелено на соседний узел состояния:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | |
target также может быть нацелен на потомка соседнего узла состояния:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
Когда целевой узел состояния является потомком исходного узла состояния, ключ исходного узла состояния можно опустить:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | |
Когда узел состояния не изменяется; то есть исходный и целевой узлы состояния одинаковы, свойство target можно опустить:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
Узлы состояний также могут быть нацелены по их id, добавив к target префикс #, за которым следует id узла состояния:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
Идентификация узлов состояний¶
Состояния могут быть идентифицированы уникальным ID: id: 'myState'. Это полезно для нацеливания на состояние из любого другого состояния, даже если они имеют разные родительские состояния:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | |
ID состояний не влияют на state.value. В приведённом выше примере state.value всё равно будет closed, хотя узел состояния идентифицирован как #finished.
Другие типы состояний¶
В диаграммах состояний есть другие типы состояний:
- Родительские состояния (также известные как составные состояния)
- Параллельные состояния
- Состояния истории
- Финальные состояния
Моделирование состояний¶
При проектировании конечных состояний для вашей машины состояний следуйте этим рекомендациям для создания поддерживаемых и эффективных машин состояний:
Начните просто и неглубоко¶
- Начните с минимального количества состояний: Не создавайте множество конечных состояний, пока не станет очевидно, что поведение вашей логики различается в зависимости от некоторого конечного состояния, в котором она может находиться.
- Избегайте преждевременной оптимизации: Начните с базовых состояний и добавляйте сложность только при необходимости.
- Предпочитайте плоские структуры изначально: Глубокая вложенность может быть добавлена позже, когда появятся паттерны.
Идентифицируйте различное поведение¶
- Разное поведение = разное состояние: Создавайте отдельные состояния, когда приложение ведёт себя по-разному в ответ на одно и то же событие.
- Одинаковое поведение = одно состояние: Если несколько «состояний» обрабатывают события одинаково, они, вероятно, должны быть одним состоянием.
- Ставьте под вопрос невозможные состояния: Спросите «может ли существовать эта комбинация условий?» Если нет, моделируйте их как отдельные состояния.
Называйте состояния чётко¶
- Используйте описательные имена: Имена состояний должны чётко описывать, что делает машина или в каком режиме она находится.
- Избегайте технического жаргона: Используйте доменно-специфический язык, который понимают заинтересованные стороны.
- Будьте последовательны: Используйте согласованные соглашения об именовании во всех ваших машинах состояний.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
Моделируйте пользовательские рабочие процессы¶
- Следуйте пути пользователя: Состояния должны отражать естественную прогрессию действий пользователя.
- Учитывайте все пути: Включайте счастливые пути, состояния ошибок и граничные случаи.
- Учитывайте состояния загрузки: Асинхронные операции часто требуют промежуточных состояний.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | |
Учитывайте временные аспекты¶
- Чувствительные ко времени состояния: Моделируйте состояния, которые существуют определённое время.
- Обработка истечения: Включайте состояния для обработки таймаутов и истечений.
- Запланированные переходы: Используйте отложенные переходы для изменений состояния на основе времени.
Группируйте связанную функциональность¶
- Используйте теги для категоризации: Группируйте состояния по общим характеристикам.
- Рассмотрите родительские состояния: Когда несколько состояний имеют общие переходы, рассмотрите их группировку под родительским состоянием.
- Разделяйте задачи: Держите разные домены или функции в отдельных состояниях.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | |
Обрабатывайте граничные случаи¶
- Недопустимые состояния: Моделируйте состояния для обработки недопустимых или неожиданных условий.
- Состояния восстановления: Предоставляйте способы восстановления из состояний ошибок.
- Резервное поведение: Включайте состояния по умолчанию для необработанных сценариев.
Проверяйте переходы состояний¶
- Убедитесь, что все переходы имеют смысл: Каждый переход состояния должен представлять допустимое изменение бизнес-логики.
- Избегайте циклических зависимостей: Будьте осторожны с состояниями, которые могут бесконечно переходить друг в друга без цели.
- Рассмотрите защиты: Используйте защиты для предотвращения недопустимых переходов, даже когда события получены.
Документируйте назначение состояний¶
- Используйте описания: Добавляйте свойства
.descriptionдля объяснения сложных состояний. - Включайте метаданные: Храните соответствующую информацию о том, что представляет каждое состояние.
- Комментируйте сложную логику: Объясняйте, почему определённые состояния существуют и что они выполняют.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | |
Конечные состояния и TypeScript¶
TypeScript
XState v5 требует TypeScript версии 5.0 или выше.
Для лучших результатов используйте последнюю версию TypeScript. Подробнее о XState и TypeScript
Вы можете строго типизировать конечные состояния вашей машины, используя функцию setup(...), которая обеспечивает отличный вывод типов TypeScript и автодополнение:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | |
При использовании setup(...).createMachine(...) TypeScript обеспечивает:
- Типобезопасное сопоставление состояний:
state.matches(...)с автодополнением для всех возможных значений состояний - Строго типизированные значения состояний:
state.valueтипизирован как объединение всех возможных имён состояний - Типобезопасный контекст: Полный вывод типов для
state.context - Типобезопасные события:
actor.send(...)принимает только определённые типы событий
Шпаргалка по конечным состояниям¶
Шпаргалка: создание конечных состояний¶
1 2 3 4 5 6 7 8 9 10 11 12 | |
Шпаргалка: конечные состояния с переходами¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | |
Шпаргалка: чтение текущего состояния¶
1 2 3 4 5 6 7 8 9 10 11 12 | |
Шпаргалка: состояния с тегами¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | |
Шпаргалка: состояния с метаданными¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | |
Шпаргалка: нацеливание состояний по ID¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
Шпаргалка: строго типизированные конечные состояния¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | |