Контекст XState¶
Хотя конечные состояния четко определены в конечных автоматах и диаграммах состояний, состояние, которое представляет количественные данные (например, произвольные строки, числа, объекты и т. д.), которые могут быть потенциально бесконечными, представлено как расширенное состояние. Это делает диаграммы состояний более полезными для реальных приложений.
В XState расширенное состояние известно как контекст (context). Ниже приведен пример использования context для имитации наполнения стакана водой:
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 | |
Текущий контекст ссылается на State как state.context:
1 2 3 4 5 6 7 8 9 | |
Начальный контекст¶
Начальный контекст указывается в свойстве context автомата:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
Для динамического контекста (то есть контекста, начальное значение которого задается извне) вы можете использовать фабричную функцию, которая создает автомат с предоставленными значениями контекста, например:
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
Для существующих автоматов следует использовать machine.withContext(...):
1 2 3 4 5 6 7 8 9 10 | |
Исходный контекст машины можно получить из ее начального состояния:
1 2 | |
Этот способ предпочтительнее прямого доступа к machine.context, так как начальное состояние вычисляется с помощью начальных действий assign(...) и проходных переходов, если таковые имеются.
Действие assign()¶
Действие assign() используется для обновления контекста автомата. Оно принимает контекст assigner, который указывает, как должны быть присвоены значения в текущем контексте.
| Параметр | Тип | Описание |
|---|---|---|
assigner | object function | Объект или функция, которые присваивают значения контексту |
assigner может быть объектом (рекомендовано):
1 2 3 4 5 6 7 8 9 10 11 12 | |
Или функция, которая возвращает обновленное состояние:
1 2 3 4 5 6 7 8 9 10 11 12 | |
Приведенная выше функция принимает три параметра: context, event и meta
| Параметр | Тип | Описание |
|---|---|---|
context | TContext | Текущий контекст (расширенное состояние) автомата |
event | EventObject | Событие, вызвавшее действие assign |
meta | AssignMeta | объект с мета-данными, начиная с версии 4.7+ |
Объект мета-данных содержит:
state— текущее состояние при нормальном переходе (undefinedдля перехода начального состояния)action— связанное действиеassign
Внимание
Функция assign(...) является создателем действия; это чистая функция, которая возвращает только объект действия и не делает обязательных присваиваний контексту.
Порядок действий¶
Пользовательские действия всегда выполняются в отношении следующего состояния в переходе. Когда переход состояния имеет действия assign(...), эти действия всегда группируются и вычисляются первыми, чтобы определить следующее состояние. Так происходит потому, что состояние - это комбинация конечного состояния и расширенного состояния (контекста).
Например, на этом счетчике пользовательские действия не будут работать должным образом:
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 | |
Это связано с тем, что оба действия assign(...) группируются по порядку и выполняются первыми (на микрошаге), поэтому следующим context состояния является {count: 2}, который передается обоим настраиваемым действиям. Другой способ думать об этом переходе - читать его так:
Когда в состоянии
activeпроисходит событиеINC_TWICE, следующее состояние — это состояниеactiveс обновленнымcontext.count, а затем эти настраиваемые действия выполняются в этом состоянии.
Хороший способ рефакторинга этого для получения желаемого результата — моделирование context с явными предыдущими значениями, если они необходимы:
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 | |
Преимущества от этого:
- Расширенное состояние (контекст) моделируется более явно
- Отсутствуют неявные промежуточные состояния, предотвращающие появление трудноуловимых ошибок.
- Порядок действий более независим (Логирование «До» может идти даже после логирования «После»!)
- Облегчает тестирование и изучение состояния
Примечания¶
- 🚫 Никогда не изменяйте контекст
contextавтомата извне. У всего есть причина, и каждое изменение контекста должно происходить явно из-за события. - Предпочтителен синтаксис объекта
assign({...}). Это позволяет будущим инструментам анализа предсказывать, как определенные свойства могут измениться декларативно. - Задания
assignможно складывать, и они будут выполняться последовательно:
1 2 3 4 5 6 | |
- Как и в случае с
actions, лучше всего представлять действияassign()в виде строк или функций, а затем ссылаться на них в параметрах автомата:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
Или через именованные функции:
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
- В идеале
contextдолжен быть представлен как простой объект JavaScript, т. е. он должен быть сериализуемым как JSON. - Поскольку вызываются действия
assign(), контекст обновляется перед выполнением других действий. Это означает, что другие действия на том же шаге получат обновленный контекст, а не тот, который был до выполнения действияassign(). Вы не должны полагаться на порядок действий для своих состояний, но имейте это в виду.
TypeScript¶
Для правильного вывода типа, добавьте тип контекста в качестве первого параметра типа в createMachine<TContext, ...>:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
Если возможно, вы также можете использовать typeof ... как сокращение:
1 2 3 4 5 6 7 8 9 10 | |
В большинстве случаев типы контекста context и события event в действиях assign(...) будут автоматически выведены из параметров типа, переданных в createMachine<TContext, TEvent>:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | |
Однако вывод TypeScript не идеален, поэтому можно добавить контекст и событие в качестве обобщений в assign<Context, Event>(...):
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
Краткий справочник¶
Установка начального контекста
1 2 3 4 5 6 7 8 | |
Установка динамического начального контекста
1 2 3 4 5 6 7 8 9 10 11 | |
Установка пользовательского контекста автомату
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
Связывание контекста
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | |
Статичное связывание
1 2 3 4 5 | |
Связывание через функцию
1 2 3 4 5 6 7 | |
Связывание через контекст
1 2 3 4 5 6 7 8 9 | |
Множественное связывание
1 2 3 4 5 6 7 8 9 | |