Events and transitions
A transition is a change from one finite state to another, triggered by an event.
An event is a signal, trigger, or message that causes a transition. When an actor receives an event, its machine will determine if there are any enabled transitions for that event in the current state. If enabled transitions exist, the machine will take them and execute their actions.
Transitions are "deterministic"; each combination of state and event always points to the same next state. When a state machine receives an event, only the active finite states are checked to see if any of them have a transition for that event. Those transitions are called enabled transitions. If there is an enabled transition, the state machine will execute the transition's actions, and then transition to the target state.
Transitions are represented by on:
in a state:
import { createMachine } from 'xstate';
const feedbackMachine = createMachine({
id: 'feedback',
initial: 'question',
states: {
question: {
on: {
'feedback.good': {
target: 'thanks',
},
},
},
thanks: {},
},
});
Event objects​
In XState, events are represented by event objects with a type
property and optional payload:
- The
type
property is a string that represents the event type. - The payload is an object that contains additional data about the event.
feedbackActor.send({
// The event type
type: 'feedback.update',
// Additional payload
feedback: 'This is great!',
rating: 5,
});
Selecting transitions​
Transitions are selected by checking the deepest child states first. If the transition is enabled (i.e. if its guard passes), it will be taken. If not, the parent state will be checked, and so on.
- Start on the deepest active state nodes (aka atomic state nodes)
- If the transition is enabled (no
guard
or itsguard
evaluates totrue
), select it. - If no transition is enabled, go up to the parent state node and repeat step 1.
- Finally, if no transitions are enabled, no transitions will be taken, and the state will not change.
Self-transitions​
A state can transition to itself. This is known as a self-transition, and is useful for changing context and/or executing actions without changing the finite state. You can also use self-transitions to restart a state.
Root self-transitions:
import { createMachine, assign } from 'xstate';
const machine = createMachine({
context: { count: 0 },
on: {
someEvent: {
// No target
actions: assign({
count: ({ context }) => context.count + 1,
}),
},
},
});
Self-transitions on states:
import { createMachine, assign } from 'xstate';
const machine = createMachine({
context: { count: 0 },
initial: 'inactive',
states: {
inactive: {
on: { activate: { target: 'active' } },
},
active: {
on: {
someEvent: {
// No target
actions: assign({
count: ({ context }) => context.count + 1,
}),
},
},
},
},
});
Transitions between states​
Usually, transitions are between two sibling states. These transitions are defined by setting the target
as the sibling state key.
const feedbackMachine = createMachine({
// ...
states: {
form: {
on: {
submit: {
// Target is the key of the sibling state
target: 'submitting',
},
},
},
submitting: {
// ...
},
},
});