Finite State Machines in React JS using xState JS
Tags: react, javascript, tutorial, xstate
Published: 2020-09-23 04:36:25

**This article was published before react hooks was introduced. so I have followed the earlier component life cycle methods**

This blog was originally published on [Skcript](https://skcript.com) and thank you https://xstate.js.org/ for adding this article on the tutorial page.

![Screenshot](https://dev-to-uploads.s3.amazonaws.com/i/iq3g1gd4pvkpy1yj523b.png)

Now back to the article :)

What’s a finite state machine? To understand what a finite state machine is we need to first know what a state machine is.

A state machine is a machine that can transition into different states on passing certain inputs to them. Now add finite states to the machine so that your machine has only a limited number of outputs. You define a finite number of inputs and outputs and your machine can only transition to one of those outputs.

![](https://media.giphy.com/media/3og0Iwmv38WmJBrYvS/giphy.gif)
“Machine goes here or there, but nowhere else”

State machines are very useful since they never go out of bounds. Whatever the input is, if the machine recognizes it as feasible, then it will transition to a proper state, else based on your configuration, your state machine will either stop transitioning or throw an error.

For this article, let’s use **xstate** a state machine interpreter package that can be used with React JS with quite an ease.

We will create a simple authentication module that will have the following,

- Login
- Home
- Server (we will use an express server for now)

For this project we will use **npm** to install all our packages.

We will first create our React App using **create-react-app finite-machine-auth.**

This will create our React App. Let’s modify it a little bit. We will create 2 Containers **Login.js**, **Home.js** and an **Authenticator.js** for authenticating Home page.

The main reason we are using state machines for our state transitions is that we want all our business logic to be on one side and all our UI on the other side. In other words, our state machine will take care of all the state transitions needed for authentication while React will render based on this transitioning state. So the code would look much more compact and debugging would be so much easier.

![](https://media.giphy.com/media/11sBLVxNs7v6WA/giphy.gif)
Developers when they hear “Debugging is easier”

Now let’s look at our finite machine

```js
import { Machine } from "xstate";

export const authMachine = Machine(
  {
    id: "authentication",
    initial: "unauthorized",
    context: {
      newLink: null,
      errorMessage: null,
    },
    states: {
      unauthorized: {
        on: {
          LOGIN: "loading",
        },
      },
      loading: {
        on: {
          LOGIN_SUCCESS: {
            target: "authorized",
            actions: ["onSuccess"],
          },
          LOGIN_ERROR: {
            target: "unauthorized",
            actions: ["onError"],
          },
        },
      },
      authorized: {
        on: {
          LOGOUT: "unauthorized",
        },
      },
    },
  },
  {
    actions: {
      onSuccess: (context, event) => {
        if (event.reverse) {
          context.newLink = "/";
        } else {
          context.newLink = null;
        }
        context.errorMessage = null;
      },
      onError: (context, event) => {
        if (event.reverse) {
          context.newLink = null;
        } else {
          context.newLink = "/login";
        }
        context.errorMessage = event.errorMessage;
      },
    },
  }
);
```

That’s it all our state logic in a single machine. **xstate** gives us a **Machine** method to actually create a machine from an object configuration. Now let’s take a look at the machine itself deeply.

- **id** - any string that can be used to identify a machine. Suppose we have more than 1 machine , we will use the id to find the machine.
- **initial** - initial value of the machine.
- **context** - context can be used to store anything in the machine and then pass it to the Components that use the machine.
- **states** - the states where the machines can transition to. The machine will never transition to any other state regardless of the input. Poof!, easy debugging!
  - Each state has an **on** state, that gets executed whenever the machine gets is in that state and the corresponding input is passed.
  - Let’s consider our code as an example. Suppose our machine has its state in **unauthorized**, and we pass an input **LOGIN** to the machine. Now the machine knows that **on{ LOGIN: ‘loading’}**. So the machine will now transition to the **loading** state. Any other input passed during the **unauthorized** state will not transition the machine, making it secure. The machine will either stop because it doesn’t know the transition or will throw an error if the configuration setting **strict: true** is used.

![](https://media.giphy.com/media/sRMPFaVQLGSw8/giphy.gif)

When you pass an input the machine doesn’t know

- Now you can also have actions that the machine needs to perform when the machine is in a certain state. That’s where **actions** come into play.
- You can call your actions in the **on** method on passing a certain input.
- You can define your actions as a different object after the states. In this example, I have created two actions, **onSuccess** and **onError**. And I have called the action from the **on** method. I have used something called a **target** because the value of the input is more than one parameter.
- The action functions will take two parameters **context, event.** So now the function can directly change the value of the context. The event object contains the value passed from the components.

For now, we will just use this configuration. In later discussions, we will use other configurations like **guards, activities, parallel states,** etc.

Before we start implementing our machine in our component, we can have a visual glimpse of our machine in action. Navigate to [https://xstate.js.org/viz/](https://xstate.js.org/viz/) and just copy-paste our state machine code over the default code.

Let’s try it out. copy-paste our code and click **Update**. Now your machine would be visualized on the left.

![](https://www.skcript.com/svrmedia/heroes/xstate/image5.png)

Now let’s try our machine. Go to the **EVENTS** tab, and enter the type as **LOGIN** and click Send. You are now telling the machine to take the input **LOGIN** and change its state based on the input. The **STATE** tab will show you the current state value and context of the machine. Let’s send **LOGIN** input to our machine.

![](https://www.skcript.com/svrmedia/heroes/xstate/image1.png)

And it’s a success. Now let’s see if our functions work as intended. We will use **LOGIN_SUCCESS** for this test.

![](https://www.skcript.com/svrmedia/heroes/xstate/image6.png)

And it’s a success again. We see that our action gets executed successfully and our context changes.

![](https://media.giphy.com/media/111ebonMs90YLu/giphy.gif)

Approved!!

So our machine seems to work as we intended it to work. Now we can start implementing with the application.

Now let’s take a look at our **Authenticator.js**

```js
import React from "react";
import { Redirect } from "react-router-dom";
import { interpret } from "xstate";
import { authMachine } from "../Automata/authMachine";
import { authenticate } from "../Models/Auth";
import Loader from "../Components/Shared/Loader";

export default class Authenticator extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      auth: authMachine.initialState,
    };
  }

  // For every transition set the state of the machine as current state
  service = interpret(authMachine).onTransition((current) => this.setState({ auth: current }));

  // Start the service when the component is mounted
  componentDidMount() {
    this.service.start();
    this.authenticate();
  }

  // Stop the service when the component is unmounted
  componentWillUnmount() {
    this.service.stop();
  }

  // Authenticator function
  authenticate = () => {
    const { send } = this.service;
    var { reverse } = this.props;
    send("LOGIN");
    authenticate()
      .then((response) => {
        send({ type: "LOGIN_SUCCESS", reverse });
      })
      .catch((err) => {
        send({ type: "LOGIN_ERROR", reverse, errorMessage: err.toString() });
      });
  };

  render() {
    let { auth } = this.state;
    if (auth.context.newLink) {
      return (
        <Redirect
          to={{
            pathname: auth.context.newLink,
          }}
        />
      );
    }
    if (auth.value === "loading") return <Loader />;
    return this.props.children;
  }
}
```

Now the API functions are just normal functions that communicate with the dummy express server that sends a jwt token for an authenticated user.

To use our state machine, we need to import our state machine to the component that we are going to use it in. In order for our state machine to work as intended, we need an interpreter to make out what the machine is doing.

Fortunately, xstate provides the **interpret** method for our ready use. We will use interpret to create a user-defined method called **service**. In this method, we will pass the machine to be interpreted to the **interpret** method. Then we will use the **onTransition** property of the machine to get the current state of the machine each time the machine changes its state.

Now we will start the service when the Component mounts and stop the service when the Component unmounts.

Now we have the machine setup completely. The only thing left for us to do is to communicate with the machine by sending inputs.

Our service method gives us a method called **Send** which allows us to send inputs to our machine. **Send** has multiple parameters, the first one being the input and the rest being the values sent to the machine.

Just run your code and you are done. Your Authenticator now has its state transitioned by the state machine. There you go.