# 模型 Models
在 XState 中,你可以使用 createModel(...) 在外部对状态机的 context 和 events 进行建模。 这提供了一种强类型context 和events 的便捷方式,以及未来事件创建、分配和其他实现细节的帮助。
使用 createModel(...) 是 完全可选的,旨在改善开发人员体验。 使用它的主要原因是:
- 以强类型的方式分离和组织
context和events - 使用
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 操作方便且类型安全的替代品。
import { createModel } from 'xstate/lib/model';
const userModel = createModel({
name: 'Someone',
age: 0
});
// ...
const machine = userModel.createMachine({
context: userModel.initialContext,
// ...
entry: userModel.assign({ name: '' })
});
# 模型 events
在模型中对状态机事件建模有两个好处:
- 可以通过调用
model.events.eventName(...)来创建事件 - 为状态机定义提供类型信息,为动作定义提供特定于事件的类型安全
import { createModel } from 'xstate/lib/model';
const userModel = createModel(
// 初始 context
{
name: 'David',
age: 30
},
{
// 创建事件
events: {
updateName: (value) => ({ value }),
updateAge: (value) => ({ value }),
anotherEvent: () => ({}) // 没有内容
}
}
);
const machine = userModel.createMachine(
{
context: userModel.initialContext,
initial: 'active',
states: {
active: {
on: {
updateName: {
actions: userModel.assign({
name: (_, event) => event.value
})
},
updateAge: {
actions: 'assignAge'
}
}
}
}
},
{
actions: {
assignAge: userModel.assign({
age: (_, event) => event.value // 推断
})
}
}
);
// 这会发送以下事件:
// {
// type: 'updateName',
// value: 'David'
// }
const nextState = machine.transition(
undefined,
userModel.events.updateName('David')
);
# TypeScript
createModel(...) 函数推断以下类型:
context从createModel(initialContext, creators)中的第一个参数推断events从createModel(initialContext, creators)中的creators.events推断
import { createModel } from 'xstate/lib/model';
const userModel = createModel(
{
name: 'David', // 推断为 `string`
age: 30, // 推断为 `number`
friends: [] as string[] // explicit type
},
{
events: {
updateName: (value: string) => ({ value }),
updateAge: (value: number) => ({ value }),
anotherEvent: () => ({}) // 没有内容
}
}
);
// Context 推断为:
// {
// name: string;
// age: number;
// friends: string[];
// }
// Events 推断为:
// | { type: 'updateName'; value: string; }
// | { type: 'updateAge'; value: number; }
// | { type: 'anotherEvent'; }
# 从 model 创建一个状态机
不应在 createMachine<TContext, TEvent>(...) 中将 context 和 event 的类型显式指定为类型参数,而应该使用 model.createMachine(...) 方法:
const machine = userModel.createMachine({
context: userModel.initialContext,
initial: 'active',
states: {
active: {
on: {
updateName: {
actions: userModel.assign({
name: (_, event) => event.value // 推断
})
}
}
}
}
});
# 缩小分配事件类型
当在options.actions中引用assign()动作时,你可以在model.assign(assignments, eventType)的第二个参数中缩小该动作接受的事件类型:
const assignAge = userModel.assign(
{
// `event.type` 仅限于 "updateAge"
age: (_, event) => event.value // 推断为 `number`
},
'updateAge' // 限制“assignAge”操作允许的`event`
);
const machine = userModel.createMachine({
context: userModel.initialContext,
initial: 'active',
states: {
active: {
on: {
updateAge: {
actions: assignAge
}
}
}
}
});
注意
分配具有缩小事件类型的动作 不能 放置在createMachine(configuration, options)中状态机选项的actions:{...}属性内。 这是因为在 options.actions 中的操作应该被假定为可能接收 任何 事件,即使状态机配置另有建议。
# 从模型中提取类型
从 4.22.1 开始
你可以使用 ContextFrom<T> 和 EventFrom<T> 类型,从模型中提取 context 和 event 类型:
import { ContextFrom, EventFrom } from 'xstate';
import { createModel } from 'xstate/lib/model';
const someModel = createModel(
{
/* ... */
},
{
events: {
/* ... */
}
}
);
type SomeContext = ContextFrom<typeof someModel>;
type SomeEvent = EventFrom<typeof someModel>;