# Hierarchical State Nodes
Statecharts, by definition, are hierarchical - that is to say, they:
- enable refinement of state
- can group similar transitions
- allow isolation
- encourage composability
- and prevent state explosion, which frequently occurs in normal finite state machines.
In XState, state and machine configuration share a common schema, which allows machines to be substates and for states to be infinitely nested.
Here's an example of a traffic light machine with nested states:
const pedestrianStates = {
initial: 'walk',
states: {
walk: {
on: {
PED_COUNTDOWN: 'wait'
}
},
wait: {
on: {
PED_COUNTDOWN: 'stop'
}
},
stop: {},
blinking: {}
}
};
const lightMachine = Machine({
key: 'light',
initial: 'green',
states: {
green: {
on: {
TIMER: 'yellow'
}
},
yellow: {
on: {
TIMER: 'red'
}
},
red: {
on: {
TIMER: 'green'
},
...pedestrianStates
}
},
on: {
POWER_OUTAGE: '.red.blinking',
POWER_RESTORED: '.red'
}
});
The 'green'
and 'yellow'
states are simple states - they have no child states. In contrast, the 'red'
state is a composite state since it is composed of substates (the pedestrianStates
).
To transition from an initial state, use machine.initialState
:
console.log(lightMachine.transition(lightMachine.initialState, 'TIMER').value);
// => 'yellow'
When a composite state is entered, its initial state is immediately entered as well. In the following example:
- the
'red'
state is entered - since
'red'
has an initial state of'walk'
, the{ red: 'walk' }
state is ultimately entered.
console.log(lightMachine.transition('yellow', 'TIMER').value);
// => {
// red: 'walk'
// }
When a simple state does not handle an event
, that event
is propagated up to its parent state to be handled. In the following example:
- the
{ red: 'stop' }
state does not handle the'TIMER'
event - the
'TIMER'
event is sent to the'red'
parent state, which does handle it.
console.log(lightMachine.transition({ red: 'stop' }, 'TIMER').value);
// => 'green'
If neither a state nor any of its ancestor (parent) states handle an event, no transition happens. In strict
mode (specified in the machine configuration), this will throw an error.
console.log(lightMachine.transition('green', 'UNKNOWN').value);
// => 'green'