Eventless (always) transitions
Eventless transitions are transitions that happen without an explicit event. These transitions are always taken when the transition is enabled.
Eventless transitions are specified on the always
state property and often referred to as “always” transitions.
import { createMachine } from 'xstate';
const machine = createMachine({
states: {
form: {
initial: 'valid',
states: {
valid: {},
invalid: {},
},
always: {
guard: 'isValid',
target: 'valid',
},
},
},
});
Eventless transitions and guards
Eventless transitions are taken immediately after normal transitions are taken. They are only taken if enabled, for example, if their guards are true. This makes eventless transitions helpful in doing things when some condition is true.
Avoid infinite loops
Eventless transitions with no target
nor guard
will cause an infinite loop. Transitions using guard
and actions
may run into an infinite loop if its guard
keeps returning true.
You should define eventless transitions either with:
target
guard
+target
guard
+actions
guard
+target
+actions
If target
is declared, the value should differ from the current state node.
When to use
Eventless transitions can be helpful when a state change is necessary, but there is no specific trigger for that change.
import { createMachine } from 'xstate';
const machine = createMachine({
id: 'kettle',
initial: 'lukewarm',
context: {
temperature: 80,
},
states: {
lukewarm: {
on: {
boil: { target: 'heating' },
},
},
heating: {
always: {
guard: ({ context }) => context.temperature > 100,
target: 'boiling',
},
},
boiling: {
entry: ['turnOffLight'],
always: {
guard: ({ context }) => context.temperature <= 100,
target: 'heating',
},
},
},
on: {
'temp.update': {
actions: ['updateTemperature'],
},
},
});
Eventless transitions and TypeScript
Eventless transitions can potentially be enabled by any event, so the event
type is the union of all possible events.
const machine = createMachine({
types: {} as {
events: { type: 'greet'; message: string } | { type: 'submit' };
},
// ...
always: {
actions: ({ event }) => {
event.type; // 'greet' | 'submit'
},
guard: ({ event }) => {
event.type; // 'greet' | 'submit'
return true;
},
},
});
Eventless transitions cheatsheet
Cheatsheet: root eventless (always) transition
import { createMachine } from 'xstate';
const machine = createMachine({
always: {
guard: 'isValid',
actions: ['doSomething'],
},
// ...
});
Cheatsheet: state eventless (always) transition
const machine = createMachine({
initial: 'start',
states: {
start: {
always: {
guard: 'isValid',
target: 'otherState',
},
},
otherState: {
/* ... */
},
},
});