@xstate/graph
This package contains graph algorithms and utilities for XState machines.
Quick Start
- Install
xstate
and @xstate/graph
:
npm install xstate @xstate/graph
- Import the graph utilities. Example:
import { Machine } from 'xstate';
import { simplePaths } from '@xstate/graph';
const machine = Machine();
const paths = simplePaths(machine);
API
getShortestPaths(machine, options?)
Arguments
machine
- the Machine
to traverseoptions
(optional) - options that customize how the algorithm will traverse the machine
Returns the shortest paths (Dijkstra's algorithm) of a machine from the initial state to every other state as a mapped object, where the:
- key is the stringified state
- value is an object with the properties:
state
- the target State
path
- the shortest path to get from the initial state to the target state
The path
is an array of segments, where each segment is an object with the properties:
state
- the State
of the segmentweight
- the total weight of the path
- Currently, each transition from one state to another has a weight of 1. This will be customizable in the future.
event
- the event object that transitions the machine
from the state to the next state in the path
Every path starts with the initial state.
The overall object structure looks like this:
{
"<SERIALIZED STATE>": {
"state": State { ... },
"path": [
{
"state": State { ... },
"event": { "type": "<event.type>", "<PROP>": "<event.PROP>" }
},
{
"state": State { ... },
"event": { "type": "<event.type>", "<PROP>": "<event.PROP>" }
},
...
]
},
...
}
Example
import { Machine } from 'xstate';
import { getShortestPaths } from '@xstate/graph';
const feedbackMachine = Machine({
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);
getSimplePaths(machine, options?)
Arguments
machine
- the Machine
to traverseoptions
(optional) - options that customize how the algorithm will traverse the machine
Returns the simple paths of a machine as a mapped object, where the:
- key is the stringified state
- value is an object with the properties:
state
- the target State
paths
- the array of paths to get from the initial state to the target state
Each path
in paths
is an array of segments, where each segment of the path is an object with the properties:
state
- the State
of the segmentevent
- the event object that transitions the machine
from the state to the next state in the path
Every path starts with the initial state.
The overall object structure looks like this:
{
"<SERIALIZED STATE>": {
"state": State { ... },
"paths": [
[
{
"state": State { ... },
"event": { "type": "<event.type>", "<PROP>": "<event.PROP>" }
},
{
"state": State { ... },
"event": { "type": "<event.type>", "<PROP>": "<event.PROP>" }
},
...
],
...
]
},
...
}
Example
import { Machine } from 'xstate';
import { getSimplePaths } from '@xstate/graph';
const feedbackMachine = Machine({
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);
Options
Options can be passed into getShortestPaths
or getSimplePaths
to customize how the graph represented by the machine should be traversed:
events
- a mapping of event types to an array of event objects to be used for those eventsfilter
- a function that determines whether a state
should be traversed. If false
, the traversal algorithm(s) will assume the state was "seen" and ignore traversing it.
Examples
In the below example, the INC
event is expanded to include two possible events, with value: 1
and value: 2
as the payload. It also ensures that the state.context.count <= 5
; otherwise, this machine would be traversed infinitely.
const counterMachine = Machine({
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);