State machine advent: Everything you need to master statecharts (24/24)
Tags: typescript, javascript, react
Published: 2019-12-24 20:51:16

# 1. Use TypeScript
Even though state machines help us eliminate lots of bugs, there can still be type errors that are difficult to catch on your own. The type definition of XState is really good. As a result, you do not only get amazing IntelliSense and autocompletion, TypeScript will shout at you whenever your machine definition is not in line with the types you created.

Another reason why I encourage everyone to use TypeScript is the fact that the types are being declared outside of the machine definition, making the machine code easy to read even for people without extensive TypeScript knowledge. I made a conscious decision to use TypeScript in most posts throughout the series and you'll find that when we got to implement the machines, all we need to do is to pass the context type, the state schema, and possible events to the `Machine` factory function. From that point, we don't have to worry about the types anymore.

```typescript
const gameMachine = Machine<GameContext, GameStateSchema, GameEvent>({
  /**
   * Almost no types will be found in here
   */
})
```

# 2. UI is a function of state, make it explicit!

Without statecharts, our business logic is spread throughout the application and states are a fuzzy mess of interdependent booleans.

If we were to render todos in an app that doesn't use deterministic states, the code might look like the following.
```jsx
{ !isLoading && !hasErrors && todos.length > 0 && (
  <ul>
    {todos.map((todo, index) => <li key={index}>{todo}</li>)}
  </ul>
  )
}
```

Going to state machines with a state structure like the following

```typescript
interface TodoStateSchema {
  states: {
    idle: {};
    loading: {};
    error: {};
    hasLoaded: {};
  }
}

interface TodoContext {
  todos: string[];
}
```

We might be tempted to refactor our code from above to something like the one below.

```tsx
{ state.matches('hasLoaded') && state.context.todos.length > 0 && (
  <ul>
    {todos.map((todo, index) => <li key={index}>{todo}</li>)}
  </ul>
  )
}
```

As we can see, we have eliminated the boolean variables and got rid of impossible states in the process (e.g `isLoading` and `hasError` being true at the same time). However, I want to point out that at times it can be better to distinctively express UI states with declarative state nodes.

We can move the conditional logic from our component to state machines by adding deeper state nodes,

```typescript
interface TodoStateSchema {
  states: {
    idle: {};
    loading: {};
    error: {};
    hasLoaded: {
      states: {
        noTodos: {};
        todos: {};
      };
    },
  }
}
```

or by refactoring to an orthogonal state structure (`request` and `has` are `parallel` state nodes).

```typescript
interface TodoStateSchema {
  states: {
    request: {
      states: {
        idle: {};
        loading: {};
        error: {};
        hasLoaded: {};
      };
    };
    has: {
      states: {
        noTodos: {};
        todos: {};
      };
    };
  }
}
```

Then we can determine the state of the machine like so:

```jsx
{ state.matches({has: 'todos'}) && (
  <ul>
    {todos.map((todo, index) => <li key={index}>{todo}</li>)}
  </ul>
  )
}
```

Using [typestates](https://xstate.js.org/docs/guides/typescript.html#typestates) which we didn't get to cover in the series, one can even enforce the condition that the machine must always have a non-empty array of todos inside the context before transitioning into the `has.todos` state.

The takeaway from this is to not be afraid to express your UI with distinct state nodes. When doing so, also don't be discouraged if certain state nodes sound weird in isolation. This is completely normal and happens usually with state nodes higher in the hierarchy (e.g `has`). The leaf state nodes or the combination of parent-child nodes are the ones that count. 

Generally speaking, the more conditional logic you can move into your machine, the fewer bugs your application will have.

# 3. Visual Studio Code tooling

*If you are using anything other than Visual Studio Code, feel free to add the extension name or config option of the editor you are using into the comments*

The first thing you'd want to install is an extension that colors your brackets. Since most of our logic is defined within the JSON machine definition, we'd want to make sure that besides indentation, a visual clue can help us to maneuver between state nodes, events, guards and any other code we put into our machines. I'm using the [Bracket Pair Colorizer 2](https://marketplace.visualstudio.com/items?itemName=CoenraadS.bracket-pair-colorizer-2) extension but have seen that some people experienced some performance issues when installing it in VSCode. Should you get hit by a significant performance penalty, try another extension that does the same thing and let us know.

Secondly, there is a command to jump the cursor from a closing bracket to the matching opening one and vice versa. This has saved me hours in finding the end of my state nodes and events. Below, you can see the default keybinding for the `editor.action.jumpToBracket` command. Feel free to bind it to a key that you can reach more easily. I personally settled on F3.

```json
{
  "key": "ctrl+m",
  "command": "editor.action.jumpToBracket",
  "when": "editorFocus"
}
```

# 4. Chrome extension

Install the [XState DevTools extension](https://chrome.google.com/webstore/detail/xstate-devtools/aamnodipnlopbknpklfoabalmobheehc?hl=en) by @amitnovick and ensure to enable the visualization for your machines.

`const [state, send] = useMachine(someMachine, { devTools: true})`

# 5. Prototype using the visualizer

Always start with defining the state structure of your statecharts. Think about what kind of responsibility every machine should have and how you could wire them to other machines using the [actor model](https://dev.to/codingdive/state-machine-advent-building-hierarchy-using-the-actor-model-and-cross-machine-communication-2ln1). I found that it is always a good idea to start modeling on paper and have recently bought a whiteboard for the same reason. When you go to the prototyping phase, use the [visualizer](https://xstate.js.org/viz/) that is also being used in the chrome extension to ensure you are not missing any transitions or states. Visual debugging is so good, you'll never want to go back to code that can't be visualized.

My workflow of writing a new state machine/statechart mostly follows the following steps:

1. Brainstorming about possible states
2. Define state schema in TypeScript
3. Implement blueprint of machines with states and possible transitions
4. Visualize and iterate over 1-3
5. Implement machines and wire them together with other existing actors
6. Wire the machine to our UI

# 6. Consume resources

Throughout the past 24 days, we've learned a lot of XState concepts and despite my attempt to explain multiple concepts on any given day, we didn't get to cover every feature of XState. In particular, [model-based testing](https://github.com/davidkpiano/xstate/tree/master/packages/xstate-test#readme), more [actor communication](https://xstate.js.org/docs/guides/actors.html#actor-api) and [activities](https://xstate.js.org/docs/guides/activities.html#interpretation) are things that I haven't written about. I highly encourage you to read through the whole [documentation](https://xstate.js.org/docs/about/concepts.html#finite-state-machines) start to finish to get a firm grasp on what is feasible with statecharts.

I haven't explored everything XState has to offer (e.g model-based testing) just yet. Once I do, I'd love to blog about it as I had a lot of fun writing the posts for this series.

Here are some of the best resources to learn more about statecharts and state machines:

- David Khourshid known as [@DavidKPiano](https://twitter.com/DavidKPiano) on social media is the creator of XState. I'm very thankful for his relentless work on XState and believe it will have the greatest positive impact on the future of web apps. Because of this and the fact that I've gotten a much better developer by watching his [public talks](https://www.youtube.com/results?search_query=David+Khourshid) and the [keyframer videocast](https://www.youtube.com/keyframers), he is one of the people I look up to the most.
- "World of Statecharts" [wiki](https://statecharts.github.io/)
- [Spectrum community](https://spectrum.chat/statecharts?tab=posts)

___

This is not an exhaustive list. Is there anything you think I should add? Let me know in the comments.

Thank you for reading the state machine/statechart series. I would love your feedback on Twitter or Telegram (@CodingDive) as they were the first 25 blog posts I've ever written.

I'm currently developing a collaborative learning and tutoring platform and want to launch it soon. Please help me design the perfect platform for you by participating in this tiny anonymous survey. https://skillfountain.app/survey