DOCS CORE_CONCEPTS Nested States (Hierarchical states)

Nested States (Hierarchical states)

Nested States (Hierarchical states)

Statecharts support hierarchical states (or nested states) as a way to deal with the state explosion problem of traditional finite state machines.

An example of a nested state machine is a crosswalk light. You give the ✋ when the stoplight is Yellow or Green. When the stoplight turns Red the crosswalk toggles to 🚶‍♀️. The crosswalk light is a nested state machine of the parent stoplight.

In Robot nested state machines are represented as separate machines with invoke used to enter the child machine.

import { createMachine, invoke, state, transition, state as final } from 'robot3';

const stopwalk = createMachine({
  walk: state(
    transition('toggle', 'dontWalk')
  ),
  dontWalk: final()
});

const stoplight = createMachine({
  green: state(
    transition('next', 'yellow')
  ),
  yellow: state(
    transition('next', 'red')
  ),
  red: invoke(stopwalk,
    transition('done', 'green')
  )
});

You can transition through these states like so:

let service = interpret(stoplight, () => {});

service.send('next');
console.log(service.machine.current); // yellow

service.send('next');
console.log(service.machine.current); // red

let child = service.child;
console.log(child.machine.current); // walk

child.send('toggle');
console.log(child.machine.current); // dontWalk
console.log(service.machine.current); // green

service.send('next');
console.log(service.machine.current); // yellow

service.send('next');
console.log(service.machine.current); // red

child = service.child;
console.log(child.machine.current); // walk

child.send('toggle');
console.log(child.machine.current); // dontWalk
console.log(service.machine.current); // green

Since nested states is a useful feature of state machines you might want to simplify how to define substates. Thanks to Robot’s composition properties it’s easy to create a way declaratively define substates as part of a machine.

Here we create a nested function that defines a submachine.

// A helper function for more easily building nested state machines.
const nested = (to, states) => invoke(
  createMachine(states),
  transition('done', to)
);

const stoplight = createMachine({
  green: state(
    transition('next', 'yellow')
  ),
  yellow: state(
    transition('next', 'red')
  ),
  red: nested('green', {
    walk: state(
      transition('toggle', 'dontWalk')
    ),
    dontWalk: final()
  })
});