Machine states and transitions

When defining a state machine we start from states and initial state.

For a toggle machine we can define the following states:

  • "On": equivalent to true
  • "Off": equivalent to false

In this example we mark the initial state as "Off".

We add states and initial state inside createMachine:

  • initial: the initial state
  • states: an object containing all the states as keys
import { setup } from "xstate";

const machine = setup({}).createMachine({
  // ๐Ÿ‘‡ Initial state (references a state)
  initial: "Off",

  // ๐Ÿ‘‡ States (object)
  states: {
    Off: {},
    On: {},
  },
});

Transitions using events

A state machine can change its current state when it receives an event.

Inside setup/types we define the events that our machine can receive:

Types for the machine are defining by using as for each property.

This pattern allows typescript to infer the type of each machine property without defining a concrete value.

import { setup } from "xstate";

type Event = { type: "toggle" };

const machine = setup({
  types: {
    events: {} as Event,
  },
}).createMachine({
  initial: "Off",
  states: {
    Off: {},
    On: {},
  },
});

Similar as we did for events in useReducer, XState requires adding a type property to each event (e.g. { type: "toggle" }).

Each state inside states is an object that defines its own properties and transitions.

When the machine receives an event, a state can intercept the event and perform a state transition:

  • When the state is "Off" and toggle is triggered then transition to "On"
  • When the state is "On" and toggle is triggered then transition to "Off"

An event is captured from a state by using on:

import { useMachine } from "@xstate/react";
import { setup } from "xstate";

type Event = { type: "toggle" };

const machine = setup({
  types: {
    events: {} as Event,
  },
}).createMachine({
  initial: "Off",
  states: {
    Off: {
      on: {
        toggle: {
          // Do something on "toggle" event
        },
      },
    },
    On: {
      on: {
        toggle: {
          // Do something on "toggle" event
        },
      },
    },
  },
});

We specify the state transition by using target:

import { setup } from "xstate";

type Event = { type: "toggle" };

const machine = setup({
  types: {
    events: {} as Event,
  },
}).createMachine({
  initial: "Off",
  states: {
    Off: {
      on: {
        toggle: { target: "On" }, // ๐Ÿ‘ˆ `Off` + `toggle` => `On`
      },
    },
    On: {
      on: {
        toggle: { target: "Off" }, // ๐Ÿ‘ˆ `On` + `toggle` => `Off`
      },
    },
  },
});

The toggle machine has two state: "On" and "Off". When the machine receives the "toggle" event it transitions from "Off" to "On" or from "On" to "Off"
The toggle machine has two state: "On" and "Off". When the machine receives the "toggle" event it transitions from "Off" to "On" or from "On" to "Off"