Watch for Vuex State changes!
Tags: vue, vuex, watch, subscribe
Published: 2019-01-07 19:34:12

---
title: Watch for Vuex State changes!
published: true
description: How to watch for Vuex State changes whenever it makes sense using Vue.watch, Vuex.watch and Vuex.subscribe!
tags: vuejs, vuex, watch, subscribe
cover_image: https://thepracticaldev.s3.amazonaws.com/i/dfe0tzzhyueymnexdayi.jpg
---
This is my first post on Dev.to so I would appreciate any feedback which could help me to improve my overall writing and also things that I might have forgotten to write and explain about! First paragraph done so let's Vue!

<hr>

Today someone asked a question on Slack about how to handle different *status* in a Vue component. What he wanted was something like that: You make a request and it has 3 basic *status* (pending/loading, success, failure/error). How to handle it in a Vue component? He asked a way to do it with Vuex (he was using Vuex) but I will take a step back as it is not necessary to use Vuex for it (but I will explore the Vuex world too).

First of all we have 3 *status* and we have to behave differently for each one of them. The snippet below shows a way of doing it:

```vue
<template>
  <h1 v-if="status === 'success'">Success</h1>
  <h1 v-else-if="status === 'error'">Error</h1>
  <h1 v-else>Loading</h1>
</template>
```

It basically displays different messages based on the *status* which is the desired behavior.

Let's first assume that it is a single component and the requested data won't be needed anywhere else (parent or sibling components) which makes the approach simple (I will explore the others later on).

I will assume you're a bit familiar with Vue.js which means you know about `created`, `methods` and `data`. Now let's implement the desired behavior for that specific component (`api.get` is mocking an API request with 1s delay so we can see the transition in the status).

```javascript
import api from '@/api';

export default {
  name: 'simple',
  data() {
    return {
      status: 'pending',
    };
  },
  created() {
    console.log(`CREATED called, status: ${this.status}`);

    this.handleCreated();
  },
  methods: {
    async handleCreated() {
      try {
        await api.get();

        this.status = 'success';
      } catch (e) {
        console.error(e);

        this.status = 'error';
      }
    },
  },
};
```

There is no big deal here as everything is handled internally in the component which was not the case from the guy that asked this question. His context was a bit different I suppose. In his case the *status* needed to be shared among other components that weren't just children of this one. In this case we might have a shared **state** and that is where Vuex comes in (You can achieve the same with `Event Bus` and it is even better than just adding Vuex for this only state).

So now let's update our component to use the *status* from the Vuex Store instead of a local value. To do so first we create the *status* state.

```javascript
export default new Vuex.Store({
  state: {
    status: 'pending',
  },
  mutations: {

  },
  actions: {

  },
});
```

Now let's update our component to use the `state.status`:

```vue
<template>
  <h1 v-if="status === 'success'">Success</h1>
  <h1 v-else-if="status === 'error'">Error</h1>
  <h1 v-else>Loading</h1>
</template>

<script>
import { mapState } from 'vuex';

export default {
  name: 'vuex1',
  computed: mapState(['status']),
};
</script>
```

Next step is to update the *status* after calling the API. We could achieve it the same way we were doing before, just referencing the *status* inside the Vuex Store but it is an extremely bad way of doing it. The right way of doing it now is dispatching a Vuex Action to handle it for us, so first we create the Action to handle it:

```javascript
export default new Vuex.Store({
  state: {
    status: 'pending',
  },
  getters: {
    status: state => state.status,
  },
  mutations: {
    updateStatus(state, status) {
      Vue.set(state, 'status', status);
    },
  },
  actions: {
    async fetchApi({ commit }) {
      try {
        await api.get();

        commit('updateStatus', 'success');
      } catch (e) {
        console.error(e);

        commit('updateStatus', 'error');
      }
    },
  },
});
```

It doesn't make sense to dispatch our Action from the component once we assumed that the State is shared among other components and we don't want each of them dispatching the same Action over and over. So we dispatch our Action in our `App.vue` file or any other component that makes sense for your application (Maybe in the main component of a view or so). Below is the snippet from the `App.vue` file dispatching the created Action:

```vue
<template>
  <div>
    <simple />
    <vuex1 />
  </div>
</template>

<script>
import Simple from '@/components/Simple.vue';
import Vuex1 from '@/components/Vuex1.vue';

export default {
  name: 'app',
  components: {
    Simple,
    Vuex1,
  },
  created() {
    this.$store.dispatch('fetchApi');
  },
};
</script>
```

Cool, now it is working as expected! But I didn't tell you a thing. The problem he was trying to solve is a bit deeper than this one. He wants that some components that are being updated by this *status* behave differently when the *status* has changed. Imagine you might want to dispatch different actions for each component once this API calls succeed, how can you achieve that while you only dispatch the actions from the components that were rendered in the page?

My intention here is to show you few possibilities to handle this situation. One thing I agree in advance is that might sound an awkward situation for most of us but try to abstract the scenario I'm presenting to you and focus on what you can achieve from the features I'm showing here (You might have a completely different scenario where this solution fits a way better than here).

## watch

Simplest way to achieve our desired solution. You can [watch](https://vuejs.org/v2/api/#watch) for a property update and handle it the way you want. In the example below we need to update a "complex" object otherwise our component will crash:

```vue
<template>
  <h1 v-if="status === 'success'">Success {{ complex.deep }}</h1>
  <h1 v-else-if="status === 'error'">Error</h1>
  <h1 v-else>Loading</h1>
</template>

<script>
import { mapState } from 'vuex';

export default {
  name: 'vuex2',
  data() {
    return {
      complex: null,
    };
  },
  computed: mapState(['status']),
  watch: {
    status(newValue, oldValue) {
      console.log(`Updating from ${oldValue} to ${newValue}`);

      // Do whatever makes sense now
      if (newValue === 'success') {
        this.complex = {
          deep: 'some deep object',
        };
      }
    },
  },
};
</script>
```

## Vuex watch

Did you know you can also use Vuex to watch for changes? Here are the [docs](https://vuex.vuejs.org/api/#watch). The only requirement is that it watches for a function that receives the State as the first param, the Getters as the second param and returns another function that will have its result watched.

There is one caveat once using Vuex watch: it returns an `unwatch` function that should be called in your `beforeDestroy` hook if you want to stop the watcher. If you don't call this function, the watcher will still be invoked which is not the desired behavior.

One thing to keep in mind here is that the reactivity takes place before the watch callback gets called which means our component will update before we set our complex object so we need to watch out here:

```vue
<template>
  <h1 v-if="status === 'success'">Success {{ complex && complex.deep }}</h1>
  <h1 v-else-if="status === 'error'">Error</h1>
  <h1 v-else>Loading</h1>
</template>

<script>
import { mapState } from 'vuex';

export default {
  name: 'vuex3',
  data() {
    return {
      complex: null,
    };
  },
  computed: mapState(['status']),
  created() {
    this.unwatch = this.$store.watch(
      (state, getters) => getters.status,
      (newValue, oldValue) => {
        console.log(`Updating from ${oldValue} to ${newValue}`);

        // Do whatever makes sense now
        if (newValue === 'success') {
          this.complex = {
            deep: 'some deep object',
          };
        }
      },
    );
  },
  beforeDestroy() {
    this.unwatch();
  },
};
</script>
```

## Vuex subscribe

You can [subscribe](https://vuex.vuejs.org/api/#subscribe) for mutations which means your handler will be called whenever a mutation is commited (You can do the same for actions with [subscribeAction](https://vuex.vuejs.org/api/#subscribeaction)). It is a bit trickier because we won't subscribe for a specific mutation only so we have to take care here.

There is one caveat once using Vuex subscribe: it returns an `unsubscribe` function that should be called in your `beforeDestroy` hook if you want to stop the subscriber. If you don't call this function, the subscriber will still be invoked which is not the desired behavior.

The drawback here is that we've lost the old value but as the first case it is called before the reactivity takes place so we avoid a double check if it is a concern. The result is shown in the snippet below:

```vue
<template>
  <h1 v-if="status === 'success'">Success {{ complex.deep }}</h1>
  <h1 v-else-if="status === 'error'">Error</h1>
  <h1 v-else>Loading</h1>
</template>

<script>
import { mapState } from 'vuex';

export default {
  name: 'vuex4',
  data() {
    return {
      complex: null,
    };
  },
  computed: mapState(['status']),
  created() {
    this.unsubscribe = this.$store.subscribe((mutation, state) => {
      if (mutation.type === 'updateStatus') {
        console.log(`Updating to ${state.status}`);

        // Do whatever makes sense now
        if (state.status === 'success') {
          this.complex = {
            deep: 'some deep object',
          };
        }
      }
    });
  },
  beforeDestroy() {
    this.unsubscribe();
  },
};
</script>
```

### Conclusion

As I mentioned earlier my idea here is not simply solving the problem that the guy from Slack came up with. I wanted to share a broader view of the solutions available and how to use them.

You may have a different problem where these solutions may have a good fit but as I did in this post here: Keep it simple! I started with a really simple solution for a specific problem and you should too. Wait until the performance issues or refactoring comes before you address complex solutions.

You can also check it out on Github if you want: [vue-listen-to-change-example](https://github.com/viniciuskneves/vue-listen-to-change-example)

#### Updates

- 23/03/2020: Added `unwatch`/`unsubscribe` calls to `beforeDestroy`. Special thanks to @opolancoh for mentioning it in the [comments](https://dev.to/opolancoh/comment/mofi).
  -  Pull request: https://github.com/viniciuskneves/vue-listen-to-change-example/pull/7