# Machines

A state machine is a finite set of states that can transition to each other deterministically due to events. A statechart is an extension of state machines; mainly, they can have:

# Configuration

State machines and statecharts alike are defined using the Machine() factory function:

import { Machine } from 'xstate';

const lightMachine = Machine({
  // Machine identifier
  id: 'light',

  // Initial state
  initial: 'green',

  // Local context for entire machine
  context: {
    elapsed: 0,
    direction: 'east'
  },

  // State definitions
  states: {
    green: {
      /* ... */
    },
    yellow: {
      /* ... */
    },
    red: {
      /* ... */
    }
  }
});

The machine config is the same as the state node config, with the addition of the following properties:

  • context - represents the local "extended state" for all of the machine's nested states. See the docs for context for more details.
  • strict - if true, ensures the following constraints are met. Defaults to false.
    • Any events that are sent to the machine but not accepted (i.e., there doesn't exist any transitions in any state for the given event) will throw an error.
    • Any unhandled Promise rejections will stop the machine (Promise Rejection/Async Invoking Callbacks).

# Options

Implementations for actions, activities, guards, and services can be referenced in the machine config as a string, and then specified as an object in the 2nd argument to Machine():

const lightMachine = Machine(
  {
    id: 'light',
    initial: 'green',
    states: {
      green: {
        // action referenced via string
        entry: 'alertGreen'
      }
    }
  },
  {
    actions: {
      // action implementation
      alertGreen: (context, event) => {
        alert('Green!');
      }
    },
    activities: {
      /* ... */
    },
    guards: {
      /* ... */
    },
    services: {
      /* ... */
    }
  }
);

This object has 4 optional properties:

  • actions - the mapping of action names to their implementation
  • activities - the mapping of activity names to their implementation
  • guards - the mapping of transition guard (cond) names to their implementation
  • services - the mapping of invoked service (src) names to their implementation

# Extending Machines

Existing machines can be extended using .withConfig(), which takes the same object structure as above:

const lightMachine = // (same as above example)

const noAlertLightMachine = lightMachine.withConfig({
  actions: {
    alertGreen: (context, event) => {
      console.log('green');
    }
  }
});

# Initial Context

As shown in the first example, the context is defined directly in the configuration itself. If you want to extend an existing machine with a different initial context, you can use .withContext() and pass in the custom context:

const lightMachine = // (same as first example)

const testLightMachine = lightMachine.withContext({
  elapsed: 1000,
  direction: 'north'
});

WARNING

This will not do a shallow merge of the original context, and will instead replace the original context with the context provided to .withContext(...). You can still "merge" contexts manually, by referencing machine.context:

const testLightMachine = lightMachine.withContext({
  // merge with original context
  ...lightMachine.context,
  elapsed: 1000
});