Todo App with Xstate and Vue composition API
Tags: vue, javascript, xstate, showdev
Published: 2020-08-03 11:07:58

In the last post, I have briefly discussed the use of State Machines and their advantages in Frontend Applications. Xstate library has made it easier in leveraging State Machines in our Frontend Applications.

I wanted to try the Xstate library so I thought of recreating my simple Todo App which was built using Vue 2. This exercise has helped me in learning about State Machines use in real applications and new features of Vue 3 which is the composition API 🤩.


##Project Walkthrough

I used the package [@vue/composition-api](https://www.npmjs.com/package/@vue/composition-api) as I was planning to recreate an existing Todo Application.

The Todo application is primarily based on the Actor Model where a new machine actor is **created by reference** with a unique identity and the parent machine is automatically subscribed to the spawned child machine's state.

```javascript
fetchList: assign({
    todoList: (context, event) => {
        return context.todoList.map((todo) => ({
            ...todo,
            ref: spawn(todoItemMachine.withContext(todo))
        }))
    }
})
```
This action on the state change of **todoMachine** will spawn a new actor of **todoItemMachine** which will be scoped to the service where it is spawned.

The classification of machines was based on the idea that actions related to TodoList like **fetch, edit, delete and create** will be in one place and the action to mark the todo as **completed** will be in one place 😉

The todoItemMachine is defined with only two actions that are completed and pending.

```javascript
const todoItemMachine = Machine({
    id: 'todoItem',
    initial: 'completed',
    states: {
        completed: {
            on: {
                completeTask: {
                    actions: 'completedTodoAction'
                }
            }
        },
        pending: {}
    },
    actions: {
        completedTodoAction: assign((context, event) => {})
    }
})
```

With this, our state logic is ready to be integrated so let's start with integration 👩🏻‍💻

I have defined a separate function to handle all the state machine actions in one place so that they can be easily shared across multiple components. Vue composition API has made it possible to keep all the logic related to one entity in one place in the form of functions instead of being separated by optional properties like the earlier versions of Vue.

```javascript
import { todoMachine } from './index'
import { useMachine } from '@xstate/vue'
import { reactive, computed } from '@vue/composition-api'
import { store } from '../store/todoActions'

export default function stateMachineActions() {
    const {
        state,
        send
    } = useMachine(todoMachine)

    let todoActionStore = reactive({
        store
    })

    function setCurrentState(state) {
        store.commit('setState', state)
    }

    function stateTransitions(action, payload) {
        send({
            type: action,
            payload
        })
    }

    return {
        state,
        stateTransitions,
        todoActionStore,
        setCurrentState,
        todoList: computed(() => state.value.context.todoList)
    }
}
```
The current state of the machine is saved in the store using the `setCurrentState` function.

The stateMachineActions function is included in the components and used whenever we want to dispatch the event and transition state from present to next.


```javascript
import stateMachineActions from './xstate-todo/generateTodoStateMachine'
export default {
    name: 'app',
    /*
     */
    setup(props, context) {
        let {
            state,
            stateTransitions,
            todoActionStore,
            setCurrentState,
            todoList
        } = stateMachineActions()


        function completeTodoItem(todoItem) {
            setCurrentState('editTodoItem')
            stateTransitions('editItem', todoItem)
        }

        return {
            state,
            createTodo,
            todoActionStore,
            todoList,
            deleteTodoItem,
            completeTodoItem
        }
    }
}
```

In order to observe any changes in the spawned actor due to state changes in the parent, machines listen to **useService** hook. In this case, if any change occurs in the todo item we can observe the **todoItemMachine** actor and update the UI accordingly.

```javascript
setup(props, context) {
        let {
            state,
            send
        } = useService(props.todo.ref)

        /*
         */
        return {
            todoItem: computed(() => state.value.context),
            isEditing,
            completeTodo,
            deleteTodo,
        }
```
ActorRef is placed within the context of the parent Machine.


You find the complete code on [Github](https://github.com/Jasmin2895/xstate-app) and CodeSandbox

{% codesandbox snowy-grass-igki6 %}

## Conclusion

This is was great fun and learning exercise. I really enjoyed exploring the library and the new Vue composition API. The official documentation along the examples helped me a lot to complete this exercise. You can refer the following documentation for more details 📝

* [Xstate documentation](https://xstate.js.org/docs/about/concepts.html#finite-state-machines)

* [Composition API RFC](https://composition-api.vuejs.org/)