Deploying state machines with frontend frameworks
Tags: webdev, xstate
Published: 2020-05-12 13:28:19

---
title: Deploying state machines with frontend frameworks
published: true
date: 2020-04-15 17:30:37 UTC
tags: webdev, xstate 
canonical_url: https://blog.logrocket.com/deploying-state-machines-with-frontend-frameworks/
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/v42iz7b8ms9psbuu2x9g.png
---
**Written by [Raphael Ugwu](https://blog.logrocket.com/author/raphaelugwu/)**✏️

There are many ways to represent state in modern web applications. In React, you can either encapsulate your state locally within components or manage your state globally using a state management library like [Redux](https://redux.js.org/). Whichever way the state of an app is represented, it is usually represented by components which can be changed to determine how the app is viewed.

When structuring apps, you may be required to have a stylesheet for each form of state your app will be in. Web apps have become increasingly more complex and creating a stylesheet for every form of state in your app could cause complications. In this blog post, we’ll learn how we can use finite state machines to create interactive and complex transitions in our apps.

## When to use finite state machines

First off, finite state machines are models that explain the behavior of a system that can only be in one state at a point in time. A state machine’s job is to read a series of inputs and then for each input, switch to a different state. Let’s say you’ve got a car that’s represented by a state machine – it’s either moving, in a state of transition (slowly stopping or slowly starting) or stationary. It can’t be in all four states at the same time and it’s also impossible for it to be neither moving, transitioning between stopping and starting, transitioning between starting and stopping, nor stationary.

When structuring a frontend app, you would have to create events and conditions with event listeners and `if` or `while` expressions for each different state your app is in. The expansion of your app could see some expressions you make contradict each other. [XState](https://xstate.js.org/docs/) lets you implement a model for each state and handles transit between different states.

[![LogRocket Free Trial Banner](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/f760c-1gpjapknnuyhu8esa3z0jga.png?resize=1200%2C280&ssl=1)](https://logrocket.com/signup/)

## Working with XState

To show how XState works with frontend frameworks, we’ll build a concept of an interview page with XState and React. This page will render a question and a sidebar that gives a user the chance to peek at the question’s answer. At any given point in time, a user is either:

- Viewing a question they’re about to answer
- Viewing the answer to a question they do not understand
- Transitioning from viewing a question to viewing an answer
- Transitioning from viewing an answer to viewing a question

For animation, and including CSS styles in our components, we’ll make use of the following:

- [@emotion/core](https://www.npmjs.com/package/@emotion/core) — A style composition library designed for writing CSS styles with JavaScript
- [gsap](https://greensock.com/gsap/) — A library for animating JavaScript objects

Assuming we’ve created a React app (see how to do that [here](https://create-react-app.dev/docs/getting-started/)), we’ll install both `XState` and `xstate/react`, a set of utilities for using XState in React apps:

```jsx
npm i xstate @xstate/react
```

## Creating our components

The first component we’ll create is a button for our transitions. In this button component, we’ll pass down props from our main component. Which will contain our state machine as well as our invoked functions:

```jsx
// Button.jsx

/** @jsx jsx */
import { css, jsx } from "@emotion/core";
export const Button = props => (
  <button
    {...props}
  >
    {props.children}
  </button>
);
```

Next, we’ll create a `Test` component which will house the body of our app as well as functions that can be called by our state machine during its transition process. Let’s define two functions – `openAnswer` and `closeAnswer` for switching between the questions and answers:

```jsx
// Test.jsx

const openAnswer = useCallback(
    (context, event) => {
      return new Promise(resolve => {
        TweenMax.to(element.current, 0.5, {
          x: 0,
          backdropFilter: "blur(2px)",
          ease: Elastic.easeOut.config(1, 1),
          onComplete: resolve
        });
      });
    },
    [element]
  );

  const closeAnswer = useCallback(
    (context, event) => {
      return new Promise(resolve => {
        TweenMax.to(element.current, 0.5, {
          x: -290,
          backdropFilter: "blur(0px)",
          ease: Elastic.easeOut.config(1, 1),
          onComplete: resolve
        });
      });
    },
    [element]
  );
```

In the code sample above, we used the [`useCallback`](https://reactjs.org/docs/hooks-reference.html#usecallback) Hook to keep track of changes and only update if a value has changed to prevent unnecessary re-renders. The image below depicts that state of the app when the Hook in `openAnswer` is triggered:

![state of the app when the hook in openAnswer is triggered](https://i1.wp.com/blog.logrocket.com/wp-content/uploads/2020/04/openanswer.png?resize=730%2C416&ssl=1)

And a similar image showing when the Hook in `closeAnswer` is triggered:

![closeAnswer is triggered](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2020/04/closeanswer.png?resize=730%2C416&ssl=1)

## Setting up our state machine and configuring our view

We’ll specify all four states our app can be in with the initial state as the view of an interview question:

```jsx
<!--Test.jsx -->

const testMachine = Machine({
  initial: 'QUESTIONS',
  states: {
    QUESTIONS: {},
    VIEWING: {},
    ANSWERS: {},
    RETURNING: {},
  },
})
```

Next, we’ll configure in our state machine, the transition path we want our app to take. We’ll go from `QUESTIONS` to `ANSWERS` with `VIEWING` and `RETURNING` acting as transitions:

```jsx
<!--Test.jsx -->

const testMachine = Machine({
  initial: 'QUESTIONS',
  states: {
    QUESTIONS: {
      on: {
        ANSWERS: 'VIEWING',
      },
    },
    VIEWING: {
      on: {
        REPEAT: 'RETURNING',
      },
    },
    ANSWERS: {
      on: {
        REPEAT: 'RETURNING',
      },
    },
    RETURNING: {
      on: {
       ANSWERS: 'VIEWING',
      },
    },
  },
})
```

To complete our state machine, we need to properly handle the transitions that occur in-between the `VIEWING` and `RETURNING` states. With its [invoke](https://xstate.js.org/docs/guides/communication.html#the-invoke-property) property, XState has the ability to create functions that can return a promise. Let’s add the functions we earlier defined for switching between questions and answers:

```jsx
<!--Test.jsx -->

const testMachine = Machine({
  initial: "QUESTIONS",
  states: {
    QUESTIONS: {
      on: {
        ANSWERS: "VIEWING"
      }
    },
    VIEWING: {
      invoke: {
        src: "openAnswer",
        onDone: { target: "ANSWERS" }
      },
      on: {
        REPEAT: "RETURNING"
      }
    },
    ANSWERS: {
      on: {
        REPEAT: "RETURNING"
      }
    },
    RETURNING: {
      invoke: {
        src: "closeAnswer",
        onDone: { target: "QUESTIONS" }
      },
      on: {
        ANSWERS: "VIEWING"
      }
    }
  }
});
```

Now we’re done defining our state, we’ll use the `useMachine` Hook to configure our state machine’s services. This exposes our app’s current state and the possible state it can be set to:

```jsx
<!--Test.jsx -->

  const [current, send] = useMachine(testMachine, {
    services: {
      openAnswer,
      closeAnswer
    }
  });
```

Next, we need to set our user interface to communicate with the state and controls we’ve created so far. In `Test.jsx`, set a ternary operator to choose what the view should be on the main page when the `ANSWERS` button is clicked:

```jsx
<!--Test.jsx -->

 const testAnswers =
 current.matches("ANSWERS") || current.matches("VIEWING") ? "REPEAT" : "ANSWERS";

 let label = testAnswers === "ANSWERS" ? "ANSWERS" : "REPEAT";
```

Next, we’ll create our button. When clicked, it should generate transitions that align with the ternary operator we just defined:

```jsx
<!-- Test.jsx -->

return (
    <div
      ref={element}
      css={css`
        color: #fff;
        z-index: 9999;
        position: absolute;
        top: 0;
        bottom: 0;
        left: 0;
        width: 380px;
        transform: translateX(-290px);
        display: grid;
        grid-template-rows: 40px auto;
        align-content: start;
        justify-content: end;
      `}
    >
      <Button
        onClick={() => {
          send(testAnswers);
        }}
      >
        {label}
      </Button>
    </div>
  );
```

In the clip below, the header that shows the state of that app transitions between both states as the button – whose state also changes according to what we set in the ternary operator – is clicked:

[Click for video here.](https://youtu.be/_FhltGQhM4A)

Lastly, we’ll create a functional parent component that handles the multiple constants in our `Test` component and returns the starting state of our app via a `useState` Hook:

```jsx
<!-- index.js -->

import { css, jsx } from "@emotion/core";
import { useState } from "react";
import { Test } from "./Test";

function App() {
  const [testStatus, setTestStatus] = useState();
  return (
    <div>
      <Test setStatus={setTestStatus} />
      <div>
          <div>
            {testStatus}
          </div>
          <div>
            <span role="img" aria-label="paper">
           📝
            </span>
            The inner diameter of a test tube can be measured accurately using a:
            <br></br>
        </div>
      </div>
    </div>
  );
}
```

We’re finally done! Here’s the finished app, note the simultaneous switching of states in all the parts involved:

[Click for video here.](https://youtu.be/s-UhMdm05RY)

## Conclusion

There’s no uniform solution when it comes to handling state in web applications. Just be sure you’ve got your architecture right and the conditions you create in each component do not contradict. XState helps in making these conditions specific. Built around the concept of finite state machines which have been around for some time, it’s very stable. Should you like to understand the code samples more, you can view a full working demo [here on CodeSandbox](https://codesandbox.io/s/interview-model-ykqks).

* * * 
## Plug: [LogRocket](https://logrocket.com/signup/), a DVR for web apps
 
![LogRocket Dashboard Free Trial Banner](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/1d0cd-1s_rmyo6nbrasp-xtvbaxfg.png?resize=1200%2C677&ssl=1)
 
[LogRocket](https://logrocket.com/signup/) is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
 
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.
 
[Try it for free](https://logrocket.com/signup/).
* * *

The post [Deploying state machines with frontend frameworks](https://blog.logrocket.com/deploying-state-machines-with-frontend-frameworks/) appeared first on [LogRocket Blog](https://blog.logrocket.com).