State machine actor building blocks

What about state machines? Can we implement a toggle logic using a state machine? Yes!

In the following lessons we are going to implement the same toggle logic also using a state machine actor.

useReducer and libraries like redux contain values inside a store, which is generally a javascript object or primitive value:

type Event = { type: "toggle" };
type Context = boolean; // 👈 Store value (primitive `boolean`)
const initialContext = false;

export const reducer = (context: Context, event: Event): Context => {
  if (event.type === "toggle") {
    return !context; // 👈 Update store value
  }
  return context;
};

Instead of working directly with a store, state machines work with states and transitions:

  • A machine has a finite number of states
  • A machine can be in only one state at a time
  • Transitions allow to change the current state

It may sound complex, but don't worry too much for now 💁🏼‍♂️

We are going to learn more about this new state-based approach as we go through the course.

State machines are often considered more complex, but I aim to show you in this course that it's just a different mental model.

Building blocks of a state machine in XState

A basic state machine in XState is defined using the createMachine function:

import { createMachine } from "xstate";

const machine = createMachine({});

Inside createMachine you specify types and configurations for the machine:

createMachine provides all the parameters necessary to implement the logic of the machine
createMachine provides all the parameters necessary to implement the logic of the machine

Nonetheless, since XState v5 the suggested way to define a machine is using the setup function instead, and then calling createMachine from it.

setup makes the machine more type-safe by defining types to guide its later implementation.

import { setup } from "xstate";

const machine = setup({}).createMachine({});

Inside setup you specify types and configurations for your machines. No need to understand each option right now, we will learn how to use them in practice when needed.

actions, actors, delays, guards, schemas, types definitions inside setup
actions, actors, delays, guards, schemas, types definitions inside setup

The main advantage of using setup is making the machine fully type-safe by defining types:

types inside setup allows to make the machine type safe
types inside setup allows to make the machine type safe

If you come from XState v4 keep in mind that XState v5 doesn't need any type generation.

All the types are defined inside setup and everything else is inferred!

As mentioned, from setup we create the concrete machine implementation using createMachine:

import { setup } from "xstate";

export const machine = setup({}).createMachine({});

Inside createMachine we define all states and transitions.