State Machines for JavaScript Developers - How to Use Them in Your Apps
Tags: javascript, webdev, programming, tutorial
Published: 2021-12-11 06:13:54

_by author [Victoria Lo](https://blog.openreplay.com/authors/victoria-lo)_

## What is a state machine?
A finite state machine is a mathematical model of computation, often used to design algorithms. It describes the behaviour of a system, which is defined in one single state at a time from a finite number of possible states.

For example, in a game, a character can have 4 possible states: `idle`, `walk`, `run`, and `jump`. The character cannot be in 2 states at the same time. The initial state is `idle`. From this state, the character can transition to `walk`, `run` or `jump` depending on the **input** received.


![char.png](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1q0dfa8eo47640rtllqi.png)

Once an input is received, only then the state can change and produce the corresponding output. Let's say in this example, we choose to `walk`. When the state changes in response to our input, `walk` becomes our new current state and the character will show its walking animation as the output.

## Why state machines?
At this point, you are probably wondering what are the advantages to implement state machines. The benefits of state machines can be seen from our example earlier:

1. There are **finite possible states** which can make your app predictable in terms of state logic: implementing what and how one state can change to another and its expected output. This also keeps your app less prone to unintended side effects or bugs.

2. Testing also becomes relatively easy to manage due to the finite number of states, events and outputs.

3. There are also state machine visualizer tools online to help easily create and visualize the state logic for your app.

![image.png](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g5gy4sjluu172bbuxcy8.png)
*Sample visualization of states using XState Visualizer*

## Implementing State Machines

Now that we have learned a little more about finite state machines, let's take a look at how we can implement a simple one in a typical app.

Introducing [XState](https://xstate.js.org/), an easy-to-use library to create, visualize and execute finite state machines. In this example, let's create a state machine for our game character mentioned earlier.

### Step 1: Install and Import XState
First, we must install the library into our app.
```
npm install xstate
```
Then, we can import `createMachine` to create our first character machine as follows:

```javascript
import { createMachine } from 'xstate';

const characterMachine = createMachine({ ... });
```

### Step 2: Design and Create Machine
Using XState's Visualizer, we can determine how our state machine behaves, how it is transitioned from one state to another, and the output of each state.

For the sake of simplicity, here is the visualization of our character state machine:

![image.png](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pzsksrfalb04n2tw7olk.png)

From this diagram, we can see that our character starts at the `idle` state. When a 'WALK' transition occurs, the character then goes to its `walk` state, and so on.

Our `createMachine` function will look like:
```javascript
const characterMachine = createMachine({
  id: 'character', // identify our machine
  initial: 'idle', //our initial state
  states: {
    idle: {
      on: {
        // transition WALK changes state to 'walk'
        WALK: {target: 'walk'}
      }
    },
    walk: {
      on: {
        RUN: {target: 'run'}
      }
    },
    run: {
      on: {
        JUMP: {target: 'jump'}
      }
    },
    jump: {
      on: {
        IDLE: {target: 'idle'}
      }
    }
  }
});
```
Let's walk through this code slowly. The `initial` property value is our `idle` state, as illustrated in the visualizer. Under `states`, we have our 4 states: `idle`, `walk`, `run` and `jump`.

In each child state, we have the `on` property, which includes **state transition objects** that defines the target state. For example, under `idle`, its `on` property describes that the 'WALK' transition would change the state to `walk`.

### Open Source Session Replay
Debugging a web application in production may be challenging and time-consuming. [OpenReplay](https://github.com/openreplay/openreplay) is an Open-source alternative to FullStory, LogRocket and Hotjar. It allows you to monitor and replay everything your users do and shows how your app behaves for every issue.
It’s like having your browser’s inspector open while looking over your user’s shoulder.
OpenReplay is the only open-source alternative currently available.

![OpenReplay](https://raw.githubusercontent.com/openreplay/openreplay/main/static/replayer.png)

Happy debugging, for modern frontend teams - [Start monitoring your web app for free](https://github.com/openreplay/openreplay).

### Step 3: Define Actions
We have now a machine that can only be at one state at a time, can accept an input and transitions to the next target state. But what's the use of creating this machine when there's no output or result that happens from changing states?

By adding an `action` property under each child state's transition, we can dispatch actions to execute certain output when the machine is **on the particular transition** of that state.

In our `characterMachine`, we would like the character's animation to switch between the 4 states. So we should have `actions` in each state transition object like so:
```javascript
 states: {
    idle: {
      on: {
        WALK: {
          target:'walk',
          actions: 'setAnimation' //string representing action
        }
      }
    },
```
The action `setAnimation` can be defined in the `actions` object, which we will pass as a **2nd argument** in our `createMachine` function:
```javascript
const characterMachine = createMachine({
  id: 'character',
  initial: 'idle',
  states: {...}
},
{
// action object
  actions: {
    //our setAnimation action defined here
    setAnimation: (context, event) => {
      // possible values of "event": {type: WALK}, {type: RUN}, {type: JUMP}, {type: IDLE}
      console.log(event);

      //some logic to change animation below
    }
  }
});
```
As seen in the code above, our action takes in 2 parameters: `context` and `event`. You can read more about context [here](https://xstate.js.org/docs/guides/context.html#initial-context). In this example, we only need to know the `event` object.

The `event` object simply contains a `type` property with the value of the event. Knowing this, the `setAnimation` action can change the character to the correct animation. So if a `WALK` event is passed, our action executes a walking animation and the same goes for the other events.

Our final machine should look as follows:
```javascript
const characterMachine = createMachine({
  id: 'character',
  initial: 'idle',
  states: {
    idle: {
      on: {
        WALK: {
          target:'walk',
          actions: 'setAnimation'
        }
      }
    },
    walk: {
      on: {
        RUN: {
          target:'run',
          actions: 'setAnimation'
        }
      }
    },
    run: {
      on: {
        JUMP:{
          target:'jump',
          actions: 'setAnimation'
        }
      }
    },
    jump: {
      on: {
        IDLE:{
          target: 'idle',
          actions: 'setAnimation'
        }
      }
    }
  }
},
{
  actions: {
    setAnimation: (context, event) => {
      console.log(event);
      //some logic to change animation below
    }
  }
});
```

### Step 4: Run Machine

To run our machine, we first need to import the `interpret` function. Let's go back and add that in our import statement.

```javascript
import { createMachine, interpret  /*add me*/} from "xstate";
```

Next, below our `createMachine`, we initialize a `charService` to interpret and run our `characterMachine`.

```javascript
const charService = interpret(characterMachine);
```
This creates a service to run our machine. Now, we can simply start the service with:

```javascript
// start the service
charService.start();
```

 ### Step 5: Send Events
There it is! Our very first machine running. There are many ways in which we can trigger events to go from one state to another. For example, we can use the `send` method to send the event.

```javascript
charService.send({type: 'WALK'});
```
Perhaps we want the character to transition to its `walk` state when the player presses the "A" key:
```javascript
document.addEventListener('keydown', e=>{
   // if player pressed the "A" key, send the WALK event
  if (e.keyCode == 65) {
       charService.send({type: 'WALK'});
  }
})
```

## Conclusion
In this article, we've learned the basics about state machines: what it is, its characteristics, benefits to use it and how to implement it using XState.

I hope learning the concept behind state machines can help you better design algorithms and state logic for your apps. Thanks for reading! As always, feel free to like and share if the article has been helpful. 

_Till next time, cheers!_


-----------------------
### Images
- craftpix.net
- https://xstate.js.org/