In this blog post, we will introduce XState, a popular JavaScript library for working with finite state machines. Finite state machines are a useful tool for managing complex state transitions in your code and ensuring bug-free software development.

To get started with XState, you can install it using npm:

npm install xstate

Once installed, you can import the necessary functions (Machine and interpret) using the ES Modules syntax:

import { Machine, interpret } from 'xstate'

Alternatively, if you’re working in the browser, you can import XState directly from a CDN:

<script src="https://unpkg.com/xstate/dist/xstate.js"></script>

Now, let’s define a finite state machine using the Machine factory function. This function takes a configuration object as input and returns a reference to the newly created state machine.

For example, let’s create a simple traffic lights state machine:

const machine = Machine({
  id: 'trafficlights',
  initial: 'green',
  states: {
    green: {
      on: { TIMER: 'yellow' }
    },
    yellow: {
      on: { TIMER: 'red' }
    },
    red: {
      on: { TIMER: 'green' }
    }
  }
})

In this example, we have three states: green, yellow, and red. We define transitions between these states using the on property, specifying the event that triggers each transition.

To transition between states, you can use the transition() method of the state machine:

const currentState = machine.initialState.value
const newState = machine.transition(currentState, 'TIMER')

By keeping track of the current state and calling the transition() method, you can switch between states and obtain the new state object.

However, XState provides a more convenient way to work with state machines using services. To create a service, you can use the interpret() method and start it:

const toggleService = interpret(machine).start()
toggleService.send('TOGGLE')

With services, you can use the send() method to trigger state transitions without explicitly passing the current state.

Furthermore, XState allows you to define actions that are executed when a state transition occurs. These actions can be defined within the same object passed to the Machine() factory function:

const machine = Machine(
  {
    id: 'roomlights',
    initial: 'nolights',
    states: {
      nolights: {
        on: {
          p1: {
            target: 'l1',
            actions: 'turnOnL1'
          },
          p2: {
            target: 'l1',
            actions: 'turnOnL1'
          }
        }
      },
      // ... other states and transitions
    }
  },
  {
    actions: {
      turnOnL1: (context, event) => {
        console.log('turnOnL1')
      },
      // ... other action definitions
    }
  }
)

In this example, we define actions to be executed when transitioning between states. Actions can be single functions or arrays of functions.

This covers the basic usage of XState, but there is much more to explore. We recommend referring to the XState documentation for more advanced features and usage examples.