@xstate/graph
@xstate/graph 包 包含用于 XState 状态机的图算法和工具。
快速开始
- 安装
xstate
和@xstate/graph
:
npm install xstate @xstate/graph
- 导入图形工具。例如:
import { createMachine } from 'xstate';
import { getSimplePaths } from '@xstate/graph';
const machine = createMachine(/* ... */);
const paths = getSimplePaths(machine);
API
getShortestPaths(machine, options?)
参数
返回从初始状态到每个其他状态的 最短路径 (Dijkstra 算法) 的映射对象,其中:
- 键 是字符串化的状态
- 值 是一个包含以下属性的对象:
state
- 目标State
path
- 从初始状态到目标状态的最短路径
path
是一个段的数组,每个段是一个包含以下属性的对象:
state
- 段的State
weight
- 路径的总 权重- 目前,从一个状态到另一个状态的每次转换的权重为 1。未来将可自定义。
event
- 将machine
从一个状态转换到路径中下一个状态的事件对象
每条路径都以初始状态开始。
整体对象结构如下:
{
"<SERIALIZED STATE>": {
"state": State { ... },
"path": [
{
"state": State { ... },
"event": { "type": "<event.type>", "<PROP>": "<event.PROP>" }
},
{
"state": State { ... },
"event": { "type": "<event.type>", "<PROP>": "<event.PROP>" }
},
...
]
},
...
}
示例
import { createMachine } from 'xstate';
import { getShortestPaths } from '@xstate/graph';
const feedbackMachine = createMachine({
id: 'feedback',
initial: 'question',
states: {
question: {
on: {
CLICK_GOOD: 'thanks',
CLICK_BAD: 'form',
CLOSE: 'closed',
ESC: 'closed',
},
},
form: {
on: {
SUBMIT: 'thanks',
CLOSE: 'closed',
ESC: 'closed',
},
},
thanks: {
on: {
CLOSE: 'closed',
ESC: 'closed',
},
},
closed: {
type: 'final',
},
},
});
const shortestPaths = getShortestPaths(feedbackMachine);
console.log(shortestPaths);
// => {
// '"question"': {
// state: State { value: 'question', context: undefined },
// weight: 0,
// path: []
// },
// '"thanks"': {
// state: State { value: 'thanks', context: undefined },
// weight: 1,
// path: [
// {
// state: State { value: 'question', context: undefined },
// event: { type: 'CLICK_GOOD' }
// }
// ]
// },
// '"form"': {
// state: State { value: 'form', context: undefined },
// weight: 1,
// path: [
// {
// state: State { value: 'question', context: undefined },
// event: { type: 'CLICK_BAD' }
// }
// ]
// },
// '"closed"': {
// state: State { value: 'closed', context: undefined },
// weight: 1,
// path: [
// {
// state: State { value: 'question', context: undefined },
// event: { type: 'CLOSE' }
// }
// ]
// }
// };
getSimplePaths(machine, options?)
参数
返回 简单路径 的映射对象,其中:
- 键 是字符串化的状态
- 值 是一个包含以下属性的对象:
state
- 目标State
paths
- 从初始状态到目标状态的路径数组
每个 path
是一个段的数组,每个段是一个包含以下属性的对象:
state
- 段的State
event
- 将machine
从一个状态转换到路径中下一个状态的事件对象
每条路径都以初始状态开始。
整体对象结构如下:
{
"<SERIALIZED STATE>": {
"state": State { ... },
"paths": [
[
{
"state": State { ... },
"event": { "type": "<event.type>", "<PROP>": "<event.PROP>" }
},
{
"state": State { ... },
"event": { "type": "<event.type>", "<PROP>": "<event.PROP>" }
},
...
],
...
]
},
...
}
示例
import { createMachine } from 'xstate';
import { getSimplePaths } from '@xstate/graph';
const feedbackMachine = createMachine({
id: 'feedback',
initial: 'question',
states: {
question: {
on: {
CLICK_GOOD: 'thanks',
CLICK_BAD: 'form',
CLOSE: 'closed',
ESC: 'closed',
},
},
form: {
on: {
SUBMIT: 'thanks',
CLOSE: 'closed',
ESC: 'closed',
},
},
thanks: {
on: {
CLOSE: 'closed',
ESC: 'closed',
},
},
closed: {
type: 'final',
},
},
});
const simplePaths = getSimplePaths(feedbackMachine);
console.log(simplePaths);
// => {
// '"question"': {
// state: { value: 'question', context: undefined },
// paths: [[]]
// },
// '"thanks"': {
// state: { value: 'thanks', context: undefined },
// paths: [
// [
// {
// state: { value: 'question', context: undefined },
// event: { type: 'CLICK_GOOD' }
// }
// ],
// [
// {
// state: { value: 'question', context: undefined },
// event: { type: 'CLICK_BAD' }
// },
// {
// state: { value: 'form', context: undefined },
// event: { type: 'SUBMIT' }
// }
// ]
// ]
// },
// '"closed"': {
// state: { value: 'closed', context: undefined },
// paths: [
// [
// {
// state: { value: 'question', context: undefined },
// event: { type: 'CLICK_GOOD' }
// },
// {
// state: { value: 'thanks', context: undefined },
// event: { type: 'CLOSE' }
// }
// ],
// [
// {
// state: { value: 'question', context: undefined },
// event: { type: 'CLICK_GOOD' }
// },
// {
// state: { value: 'thanks', context: undefined },
// event: { type: 'ESC' }
// }
// ],
// ...
// ]
// },
// ...
// };
getPathFromEvents(machine, events)
参数:
machine
- 要遍历的Machine
events
- 生成路径的事件序列
返回一个包含以下键的路径对象:
state
- 目标State
segments
- 一个对象数组,每个对象具有以下结构:state
- 段的State
event
- 将machine
从该状态转换到路径中下一个状态的事件对象
import { createMachine } from 'xstate';
import { getSimplePaths } from '@xstate/graph';
const feedbackMachine = createMachine({
id: 'feedback',
initial: 'question',
states: {
question: {
on: {
CLICK_GOOD: 'thanks',
CLICK_BAD: 'form',
CLOSE: 'closed',
ESC: 'closed',
},
},
form: {
on: {
SUBMIT: 'thanks',
CLOSE: 'closed',
ESC: 'closed',
},
},
thanks: {
on: {
CLOSE: 'closed',
ESC: 'closed',
},
},
closed: {
type: 'final',
},
},
});
const path = getPathFromEvents(feedbackMachine, [
{ type: 'CLICK_GOOD' },
{ type: 'SUBMIT' },
{ type: 'CLOSE' },
]);
console.log(path);
// => {
// state: { value: 'closed' },
// segments: [
// {
// state: { value: 'question' },
// event: { type: 'CLICK_GOOD' },
// },
// {
// state: { value: 'form' },
// event: { type: 'SUBMIT' },
// },
// {
// state: { value: 'thanks' },
// event: { type: 'CLOSE' },
// },
// ],
// }
toDirectedGraph(machine)
将 machine
转换为有向图结构。
参数 | 类型 | 描述 |
---|---|---|
machine | 由 createMachine(...) 创建的 XState 机器 | 要转换为有向图结构的机器 |
示例
import { toDirectedGraph } from '@xstate/graph';
const machine = createMachine({/* ... */});
const digraph = toDirectedGraph(machine);
// 返回一个具有以下结构的对象:
{
id: '...',
stateNode: /* StateNode */,
children: [
{ id: '...', children: [/* ... */], edges: [/* ... */] },
{ id: '...', /* ... */ },
// ...
],
edges: [
{ source: /* ... */, target: /* ... */, transition: /* ... */ }
// ...
]
}
选项
可以将选项传递给 getShortestPaths
或 getSimplePaths
以自定义如何遍历由状态机表示的图:
events
- 事件类型到事件对象数组的映射,用于这些事件filter
- 一个函数,用于确定是否应遍历state
。如果返回false
,遍历算法将假定状态已被“看到”并忽略对其的遍历。
示例
在下面的示例中,INC
事件被扩展为包含两个可能的事件,分别为 value: 1
和 value: 2
作为负载。它还确保 state.context.count <= 5
;否则,这个状态机将被无限遍历。
const counterMachine = createMachine({
id: 'counter',
initial: 'active',
context: { count: 0 },
states: {
active: {
on: {
INC: {
actions: assign({ count: (ctx, e) => ctx.count + e.value }),
},
},
},
},
});
const shortestPaths = getShortestPaths(counterMachine, {
events: {
INC: [
{ type: 'INC', value: 1 },
{ type: 'INC', value: 2 },
],
},
filter: (state) => state.context.count <= 5,
});
console.log(shortestPaths);
// => {
// '"active" | {"count":0}': {
// state: { value: 'active', context: { count: 0 } },
// weight: 0,
// path: []
// },
// '"active" | {"count":1}': {
// state: { value: 'active', context: { count: 1 } },
// weight: 1,
// path: [
// {
// state: { value: 'active', context: { count: 0 } },
// event: { type: 'INC', value: 1 }
// }
// ]
// },
// '"active" | {"count":2}': {
// state: { value: 'active', context: { count: 2 } },
// weight: 1,
// path: [
// {
// state: { value: 'active', context: { count: 0 } },
// event: { type: 'INC', value: 2 }
// }
// ]
// },
// '"active" | {"count":3}': {
// state: { value: 'active', context: { count: 3 } },
// weight: 2,
// path: [
// {
// state: { value: 'active', context: { count: 0 } },
// event: { type: 'INC', value: 1 }
// },
// {
// state: { value: 'active', context: { count: 1 } },
// event: { type: 'INC', value: 2 }
// }
// ]
// },
// ...
// };