DEV Community

Mikey Stengel
Mikey Stengel

Posted on • Updated on

State machine advent: Introduction to nested and parallel states using statecharts (9/24)

Yesterday, I wrote about the biggest difficulty of state machines - state explosions. When David Harel published his paper Statecharts: A visual formalism for complex systems in 1987, he did not only fix the problem of state explosions, he essentially created state machines on steroids. Today, we'll cover how to define such statecharts.

Limited by the fact that our state architecture in state machines needed to follow a sequential structure, we had to duplicate a lot of transitions.

Statecharts do not have this limitation as they allow us to define parallel state regions and nested states.

interface VideoChatStateSchema {
  states: {
    audio: {
      states: {
        enabled: {};
        disabled: {};
      };
    };
    video: {
      states: {
        enabled: {};
        disabled: {};
      };
    };
  };
}

As seen in the TypeScript definition above, enabled/disabled are child state nodes of video and audio. The parent nodes are orthogonal to each other which means both states are fully independent and don't even know about the other's existence. Naturally, to let XState know that the video and audio states are not a sequence but exist at the same time, we set the type property in our machine definition to parallel.

type VideoChatEvent =
  | { type: 'ENABLE_AUDIO'}
  | { type: 'DISABLE_AUDIO'}
  | { type: 'ENABLE_VIDEO'}
  | { type: 'DISABLE_VIDEO'};


const videoChatMachine = Machine<any, VideoChatStateSchema, VideoChatEvent>({
  id: 'videoChat',
  type: 'parallel',
  states: {
    audio: {
      initial: 'disabled',
      states: {
        disabled: {
          on: {
            ENABLE_AUDIO: 'enabled'
          }
        },
        enabled: {
          on: {
            DISABLE_AUDIO: 'disabled'
          }
        },
      }
    },
    video: {
      initial: 'disabled',
      states: {
        disabled: {
          on: {
            ENABLE_VIDEO: 'enabled'
          }
        },
        enabled: {
          on: {
            DISABLE_VIDEO: 'disabled'
          }
        },
      }
    },
  }
});

If you've read the posts from the previous days, you'll see that our XState definition barely changed. XState allows us to define state machines and statecharts with the same API. Conceptually, we should think about each parallel state node as a tiny state machine. Both of them need an initial state and have states + events that drive their particular state changes.

Above all, the most compelling argument for statecharts can be seen by looking at the graph from yesterday's implementation and the one from today. A world of difference.

Yesterday using pure state machines

Visualization of https://xstate.js.org/viz/?gist=0f5591a79bf9440e0d475cd1c12b315a

Today using a statechart with parallel states

Visualization of https://xstate.js.org/viz/?gist=70779d5186afc6585b0dd1bb49764fbb

As our video chat requirements grow and we'd like to add screen sharing, we can simply add another parallel node to our statechart without worrying about state explosions.

If you are curious about the original paper from David I mentioned at the very beginning, you can find the pdf here.

About this series

Throughout the first 24 days of December, I'll publish a small blog post each day teaching you about the ins and outs of state machines and statecharts.

The first couple of days will be spent on the fundamentals before we'll progress to more advanced concepts.

Discussion (0)