Using Vue Observable as a State Store
Tags: vue, javascript, tutorial, webdev
Published: 2019-11-16 03:25:11

---
canonical_url: https://austincooper.dev/2019/08/09/vue-observable-state-store/
---

Original post here: https://austincooper.dev/2019/08/09/vue-observable-state-store/

Version 2.6.0 of Vue.js added [`Vue.Observable`](https://vuejs.org/v2/api/#Vue-observable). This is a function that returns a reactive instance of a given object. In Vue, objects are not automatically reactive. That means that if we want to _react_ when properties on an object change, we need to do some extra work. `Vue.Observable` makes that super easy. Read more about reactivity
[here](https://vuejs.org/v2/guide/reactivity.html).

### Non-Reactivity

```js
const obj = {x: 0, y: 0};
export default { // Vue component
    data() { return {}; },
    method() {
        updateObj() {
            obj.x = 1;
            obj.y = 2;
        }
    }
}
```

In this example, calling `updateObj` will not trigger a re-calculation of computed values nor re-render the view. Fortunately, Vue components have the `data` function. The object returned by `data` _is_ reactive!

### Reactivity via data()

```js
export default { // Vue component
    data() {
        return {
            obj: {x: 0, y: 0}
        };
    },
    method() {
        updateObj() {
            this.obj.x = 1;
            this.obj.y = 2;
        }
    }
}
```

Since the result of `data` is made reactive, calling `updateObj` will cause computed values that depend on `obj` to be re-calculated and update the view if needed.

## Breakout State from Components

In basic components/applications, all mutable data is put into the object returned by the `data` function. Storing all of the app's data in `data` functions on each component quickly falls apart. In particular, this becomes an issue when data needs to be passed between sibling components.

Does the component with the data pass it up to the parent component via events and then the parent component passes it down to the sibling component via props? Even in simple cases, this is immediately a code smell and not a great development experience. It couples components, is difficult to test, prone to bugs, unmaintainable, and plain confusing.

This is where state stores come in.

## State Management

Vuex is the go-to state store plugin for Vue.js. Here's how [vuejs.org](https://vuex.vuejs.org/) describes Vuex:

> Vuex is a state management pattern + library for Vue.js applications. It serves as a centralized
> store for all the components in an application, with rules ensuring that the state can only be
> mutated in a predictable fashion.

That's great, but Vuex is not quite trivial to use. For one, it needs to be added as a plugin to your Vue app. Two, it is very powerful, making it daunting to get started with. Finally, many applications are simple enough to not need Vuex and all its features for state management.

So, what's the alternative to Vuex? Of course, the answer is the topic of this post: `Vue.Observable`.

## Vue.Observable as a State Store

Finally with all the background established, here's how to use `Vue.Observable` as a state store.

### store.js

```js
import Vue from 'vue';
import axios from 'axios';

const state = Vue.Observable({ // this is the magic
    radius: 0,
    color: 'red'
});

export const getters {
    radius: () => state.radius,
    color: () => state.color
}

export const mutations {
    setRadius: (val) => state.radius = val,
    setColor: (val) => state.color = val
}

export const actions {
    fetchRadiusFromApi() {
        return axios
            .get('http://localhost:5001/api/radius')
            .then((res) => {
                mutations.setRadius(res.data);
            });
    },
    fetchColorFromApi() {
        return axios
            .get('http://localhost:5001/api/color')
            .then((res) => {
                mutations.setColor(res.data);
            });
    }
}
```

Line 4, where we declare `state`, is where the important part happens. Getters and mutations are how we read and update the state. Actions are where async calls go, namely API requests. Actions commit mutations, potentially based on the results of API requests.

### component.vue

```html
<template>
    <div>
        <div>Radius: {{ radius }}</div>
        <div>Color: {{ color }}</div>
        <button @:click="setRadius(0)">Reset radius</button>
        <button @:click="fetchColorFromApi">Fetch color</button>
    </div>
</template>

<script>
    import { getters, mutations, actions } from 'store.js';

    export default {
        data() { return {}; },
        computed() {
            ...getters // radius(), color()
        },
        created() {
            this.fetchRadiusFromApi(); // fetching data right away
            this.fetchColorFromApi().then(() => {
                console.log('You can chain then after actions, if you return the request');
            });
        }
        methods() {
            ...mutations, // setRadius(val), setColor(val)
            ...actions // fetchRadiusFromApi(), fetchColorFromApi()
        }
    }
</script>
```

## Wrapping Up

That's it! Any components can just import `store.js` and share the same state. No need to use props/events to pass around data.

Bonus tip for components that don't need all the getters or need a computed value:

### component.js
```js
computed() {
    // ...getters <- instead of this, do this:
    radius() {
        return getters.radius;
    },
    diameter() {
        return getters.radius * 2;
    }
    // this component doesn't need color
}
```