Контекст 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 |
|