Replacing Vuex with XState
Tags: xstate, vue, vuex
Published: 2020-01-27 23:21:47

I have been learning XState and state machines these past few months and I absolutely love it. I decided to build an app using Vue and XState. I wanted to share this with you all, as I have seen a lot of posts, videos, tutorials, etc. on how to integrate XState with React, but not a lot for Vue.

At first, I used Vuex in conjonction with XState.  I was inspired a lot by this [article](https://www.phillipparker.io/articles/finite-state-machines-in-vue) from Phillip Parker, as well as this [Github repo](https://github.com/Sound1ab/Minipops) by the same author. I highly suggest you read the article and check out the code if you haven't already, I learned a lot from it. 

Basically, for every feature of the app, I had a Vuex module and a corresponding XState state machine. It was working well but I knew I wasn't using XState to its full potential.

After more research, I found a way of completely getting rid of Vuex and still have a global reactive state that leverages Vue's capabilities, along with sturdy Finite State Machines that make all the features that XState provides available. It more closely resembles the [Vue recipe](https://xstate.js.org/docs/recipes/vue.html) shown in XState's docs.

Instead of Vuex, I use an event bus pattern to manage global state. This means creating a new Vue instance and passing it whatever I want to share between my components. It could be only one instance for a simple app, but most apps will probably benefit from breaking it down into multiple modules (like with Vuex). 

Then you can simply pass in this Vue instance what you need from your XState machine. I wrote a function that returns a Vue instance that exposes the machine's state, context and `send()` method and reacts to changes in the machine. 

```javascript
import Vue from "vue";
import { interpret } from "xstate";

export const generateVueMachine = machine => {
	return new Vue({
		created() {
			this.service
				.onTransition(state => {
					this.current = state;
                    this.context = state.context;
					if (process.env.NODE_ENV === "development") {
						console.log(`[ ${machine.id.toUpperCase()} STATE ]`, this.current.value);
					}
				})
				.start();
		},
		data() {
			return {
				current: machine.initialState,
				context: machine.context,
				service: interpret(machine)
			};
		},
		methods: {
			send(event) {
				this.service.send(event);
			}
		}
	});
};
```
You can then write a new file, for exemple fetchMachine.js, where you create an XState machine. You use the `generateVueMachine()` function and pass it your state machine as an argument, which returns a Vue instance that you can export.

```javascript
import { Machine, assign } from "xstate";
import { generateVueMachine } from "./generateVueMachine";

const machine = Machine({ /*...machine config */ });

export const fetchMachine = generateVueMachine(machine);
```
Now I can reference this machine wherever I want in my app and react to its changes by using Vue's computed properties.

```javascript
<template>
    <button @click="onFetch" v-if="!fetchState.matches('fetching')">Fetch<button>
    <p>{{ fetchContext.fetchResult }}</p>
</template>

<script>
// fsm
import { fetchMachine } from "./fsm/fetchMachine";

export default {
	computed: {
		fetchState() {
			return fetchMachine.current;
		},
		fetchContext() {
			return fetchMachine.context;
		}
	},
    methods: {
        onFetch() {
            fetchMachine.send({type: 'FETCH'});
        }
    }
};
</script>
```
And that's it. 

Here is a [link](https://github.com/f-elix/the-strength-log) to my app's repo so you can see how I applied this in a real context (the state machine files are in `client/fsm`).

I would really appreciate any feedback on what can be done better here.

EDIT: 
I created a Vue plugin on npm to facilitate this setup an remove some of the boiler plate. You can find it at [https://github.com/f-elix/vue-xstate-plugin](https://github.com/f-elix/vue-xstate-plugin).