vuejs 2 how to watch store values from vuex

vuejs 2 how to watch store values from vuex

I am using vuex and vuejs 2 together.
I am new to vuex, I want to watch a store variable change.
I want to add the watch function in my vue component
This is what I have so far:
import Vue from ‘vue’;
import {
MY_STATE,
} from ‘./../../mutation-types’;

export default {
[MY_STATE](state, token) {
state.my_state = token;
},
};

I want to know if there are any changes in the my_state
How do I watch store.my_state in my vuejs component?

Solutions/Answers:

Solution 1:

Let’s say, for example, that you have a basket of fruits,
and each time you add or remove a fruit from the basket, you
want to (1) display info about fruit count, but you also (2) want to be notified of the count of the fruits in some fancy fashion…

fruit-count-component.vue

<template>
  <!-- We meet our first objective (1) by simply -->
  <!-- binding to the count property. -->
  <p>Fruits: {{ count }}</p>
</template>

<script>
import basket from '../resources/fruit-basket'

export default () {
  computed: {
    count () {
      return basket.state.fruits.length
      // Or return basket.getters.fruitsCount
      // (depends on your design decisions).
    }
  },
  watch: {
    count (newCount, oldCount) {
      // Our fancy notification (2).
      console.log(`We have ${newCount} fruits now, yaay!`)
    }
  }
}
</script>

Please note, that the name of the function in the watch object, must match the name of the function in the computed object. In the example above the name is count.

New and old values of a watched property will be passed into watch callback (the count function) as parameters.

The basket store could look like this:

fruit-basket.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const basket = new Vuex.Store({
  state: {
    fruits: []
  },
  getters: {
    fruitsCount (state) {
      return state.fruits.length
    }
  }
  // Obvously you would need some mutations and actions,
  // but to make example cleaner I'll skip this part.
})

export default basket

You can read more in the following resources:

Solution 2:

You should not use component’s watchers to listen to state change. I recommend you to use getters functions and then map them inside your component.

import { mapGetters } from 'vuex'

export default {
  computed: {
    ...mapGetters({
      myState: 'getMyState'
    })
  }
}

In your store:

const getters = {
  getMyState: state => state.my_state
}

You should be able to listen to any changes made to your store by using this.myState in your component.

https://vuex.vuejs.org/en/getters.html#the-mapgetters-helper

Solution 3:

As mentioned above it is not good idea to watch changes directly in store

But in some very rare cases it may be useful for someone, so i will leave this answer. For others cases, please see @gabriel-robert answer

You can do this through state.$watch. Add this in your created (or where u need this to be executed) method in component

this.$store.watch(
    function (state) {
        return state.my_state;
    },
    function () {
        //do something on data change
    },
    {
        deep: true //add this if u need to watch object properties change etc.
    }
);

More details: https://vuex.vuejs.org/en/api.html#vuex-store-instance-methods

Solution 4:

I think the asker wants to use watch with Vuex.

this.$store.watch(
      (state)=>{
        return this.$store.getters.your_getter
      },
      (val)=>{
       //something changed do something

      },
      {
        deep:true
      }
      );

Solution 5:

This is for all the people that cannot solve their problem with getters and actually really need a watcher, e.g. to talk to non-vue third party stuff (see Vue Watchers on when to use watchers).

Vue component’s watchers and computed values both also work on computed values. So it’s no different with vuex:

import { mapState } from 'vuex';

export default {
    computed: {
        ...mapState(['somestate']),
        someComputedLocalState() {
            // is triggered whenever the store state changes
            return this.somestate + ' works too';
        }
    },
    watch: {
        somestate(val, oldVal) {
            // is triggered whenever the store state changes
            console.log('do stuff', val, oldVal);
        }
    }
}

if it’s only about combining local and global state, the mapState’s doc also provides an example:

computed: {
    ...mapState({
        // to access local state with `this`, a normal function must be used
        countPlusLocalState (state) {
          return state.count + this.localCount
        }
    }
})

Solution 6:

The best way to watch store changes is to use mapGetters as Gabriel said.
But there is a case when you can’t do it through mapGetters e.g. you want to get something from store using parameter:

getters: {
  getTodoById: (state, getters) => (id) => {
    return state.todos.find(todo => todo.id === id)
  }
}

in that case you can’t use mapGetters. You may try to do something like this instead:

computed: {
    todoById() {
        return this.$store.getters.getTodoById(this.id)
    }
}

But unfortunately todoById will be updated only if this.id is changed

If you want you component update in such case use this.$store.watch solution provided by Gong. Or handle your component consciously and update this.id when you need to update todoById.