模型 Models

在 XState 中,你可以使用 createModel(...) 在外部对状态机的 contextevents 进行建模。 这提供了一种强类型contextevents 的便捷方式,以及未来事件创建、分配和其他实现细节的帮助。

使用 createModel(...)完全可选的,旨在改善开发人员体验。 使用它的主要原因是:

  • 以强类型的方式分离和组织 contextevents
  • 使用assign(...) 防止打字错误
  • 指定事件创建者以更轻松、更安全地创建事件
  • 可能与其他状态机共享模型
  • 未来的开发者体验改进,例如指定 actions、guards 等。

createModel(...)

createModel(...) 函数需要

参数 类型 描述
initialContext object context 初始值
creators (可选) object 一个包含各种事件创建者的对象

creators 对象包括以下属性:

参数 类型 描述
events object 包含事件创建者的对象

creators.events 对象的 key 是事件类型,value 是接受任意数量参数,并返回事件数据的函数。

模型 context

由于模型定义了状态机的 context,因此可以在状态机定义中使用模型的 model.initialContext 来设置其初始 context,并使用model.assign更新状态机的 context

model.assign 函数被输入到模型 context 的形态,使其成为 assign 操作方便且类型安全的替代品。

  1. import { createModel } from 'xstate/lib/model';
  2. const userModel = createModel({
  3. name: 'Someone',
  4. age: 0
  5. });
  6. // ...
  7. const machine = userModel.createMachine({
  8. context: userModel.initialContext,
  9. // ...
  10. entry: userModel.assign({ name: '' })
  11. });

模型 events

在模型中对状态机事件建模有两个好处:

  • 可以通过调用model.events.eventName(...)来创建事件
  • 为状态机定义提供类型信息,为动作定义提供特定于事件的类型安全
  1. import { createModel } from 'xstate/lib/model';
  2. const userModel = createModel(
  3. // 初始 context
  4. {
  5. name: 'David',
  6. age: 30
  7. },
  8. {
  9. // 创建事件
  10. events: {
  11. updateName: (value) => ({ value }),
  12. updateAge: (value) => ({ value }),
  13. anotherEvent: () => ({}) // 没有内容
  14. }
  15. }
  16. );
  17. const machine = userModel.createMachine(
  18. {
  19. context: userModel.initialContext,
  20. initial: 'active',
  21. states: {
  22. active: {
  23. on: {
  24. updateName: {
  25. actions: userModel.assign({
  26. name: (_, event) => event.value
  27. })
  28. },
  29. updateAge: {
  30. actions: 'assignAge'
  31. }
  32. }
  33. }
  34. }
  35. },
  36. {
  37. actions: {
  38. assignAge: userModel.assign({
  39. age: (_, event) => event.value // 推断
  40. })
  41. }
  42. }
  43. );
  44. // 这会发送以下事件:
  45. // {
  46. // type: 'updateName',
  47. // value: 'David'
  48. // }
  49. const nextState = machine.transition(
  50. undefined,
  51. userModel.events.updateName('David')
  52. );

TypeScript

createModel(...) 函数推断以下类型:

  • contextcreateModel(initialContext, creators) 中的第一个参数推断
  • eventscreateModel(initialContext, creators) 中的 creators.events 推断
  1. import { createModel } from 'xstate/lib/model';
  2. const userModel = createModel(
  3. {
  4. name: 'David', // 推断为 `string`
  5. age: 30, // 推断为 `number`
  6. friends: [] as string[] // explicit type
  7. },
  8. {
  9. events: {
  10. updateName: (value: string) => ({ value }),
  11. updateAge: (value: number) => ({ value }),
  12. anotherEvent: () => ({}) // 没有内容
  13. }
  14. }
  15. );
  16. // Context 推断为:
  17. // {
  18. // name: string;
  19. // age: number;
  20. // friends: string[];
  21. // }
  22. // Events 推断为:
  23. // | { type: 'updateName'; value: string; }
  24. // | { type: 'updateAge'; value: number; }
  25. // | { type: 'anotherEvent'; }

从 model 创建一个状态机

不应在 createMachine<TContext, TEvent>(...) 中将 contextevent 的类型显式指定为类型参数,而应该使用 model.createMachine(...) 方法:

```ts {0} const machine = userModel.createMachine({ context: userModel.initialContext, initial: ‘active’, states: { active: { on: { updateName: { actions: userModel.assign({ name: (_, event) => event.value // 推断 }) } } } } });

  1. ### 缩小分配事件类型
  2. 当在`options.actions`中引用`assign()`动作时,你可以在`model.assign(assignments, eventType)`的第二个参数中缩小该动作接受的事件类型:
  3. ```ts
  4. const assignAge = userModel.assign(
  5. {
  6. // `event.type` 仅限于 "updateAge"
  7. age: (_, event) => event.value // 推断为 `number`
  8. },
  9. 'updateAge' // 限制“assignAge”操作允许的`event`
  10. );
  11. const machine = userModel.createMachine({
  12. context: userModel.initialContext,
  13. initial: 'active',
  14. states: {
  15. active: {
  16. on: {
  17. updateAge: {
  18. actions: assignAge
  19. }
  20. }
  21. }
  22. }
  23. });

::: warning 分配具有缩小事件类型的动作 不能 放置在createMachine(configuration, options)中状态机选项的actions:{...}属性内。 这是因为在 options.actions 中的操作应该被假定为可能接收 任何 事件,即使状态机配置另有建议。 :::

从模型中提取类型

从 4.22.1 开始

你可以使用 ContextFrom<T>EventFrom<T> 类型,从模型中提取 contextevent 类型:

```ts {1,15-16} import { ContextFrom, EventFrom } from ‘xstate’; import { createModel } from ‘xstate/lib/model’;

const someModel = createModel( { // }, { events: { // } } );

type SomeContext = ContextFrom; type SomeEvent = EventFrom; ```