1

How can I detect changes of the getter value inside the template? I have the following component:

  computed: {
    ...mapGetters({
      processingStep: 'products/processingStep',
    }),

<div class="col" v-if="processingStep !=='last'">
...
...
</div>

So when I click the button in the Vuex state value for processingStep is getting changed during the time. The thing is that inside Vue dev tools I see updated getter value but my componnet template does not track it. How can this be fixed and how can above div be aware about processingStep value change?

  • Where in the devtools do you see this updated changes? Inside vuex tab or the component tab? I suspect you might not have the correct namespace/path to "processingStep" – Sølve T. Sep 04 '20 at 09:42
  • inside vuex bindings –  Sep 04 '20 at 09:44
  • How are you changing your vuex value? are you assigning it like this: `state.step = 'last'`? (using equals) – Sølve T. Sep 04 '20 at 09:47
  • Nope committing mutation inside the action which is used inside button click. –  Sep 04 '20 at 09:59
  • Can you please show how it might work with watch and getter inside the component? –  Sep 04 '20 at 10:01
  • @Dave As described in [my comment earlier](https://stackoverflow.com/questions/63738533/how-to-get-updated-value-from-vuex-getter-inside-component-template/63738685#comment112710826_63738685), you can switch `mapGetters` to `mapState` and then use the `watch` property on your Vue instance to watch for state changes. – Ollie Sep 04 '20 at 10:22

2 Answers2

1

You should be able to subscribe to your store's mutation. Subscribing is essentially like creating an event listener for any time a mutation is called on your store. For example, if your processingStep value is changed by a mutation called setProcessingStep:

export default {
  data() {
    return {
      processingStep: null
    }
  },
  mounted() {
    this.$store.subscribe((mutation, state) => {
      if (mutation.type === 'setProcessingStep') {
        this.processingStep = state.processingStep
      }
    })
  }
}
Ollie
  • 1,355
  • 1
  • 10
  • 22
  • Why not use a watcher for the exact same thing? A getter should be reactive, so there should be no need to do any of this. – Sølve T. Sep 04 '20 at 09:44
  • mounted calls once I don't think the above solution will work as expected. –  Sep 04 '20 at 09:45
  • 1
    @Dave it only needs to call once because you are simply registering a subscribe callback – Ollie Sep 04 '20 at 09:46
  • @SølveTornøe I believe you can use `watch` if you are mapping state, but not if you are mapping getters. Getters are functions so Vue doesn't react to underlying changes to the state variable that the getter is returning. Another valid solution to this problem would be to switch `mapGetters` to `mapState` and then assign a watcher – Ollie Sep 04 '20 at 09:48
  • 1
    @Dave If the solution provided does not work for you, can you explain what happened? Provide error messages / behaviour compared to what was expected? – Ollie Sep 04 '20 at 09:49
1

This is a basic example on how this should work. I tried to mock your setup. Below is a working example of a getter being reactive to change the vue v-if in the DOM.

Maybe this example helps you spot an error you might have made in your code.

I will not add the example using a watcher, due to this being the correct way to use vuex with getters. Using a watcher would be avoiding the real problem and be considered bad practice.

I also suspect you might have broken the vue reactivity in your app. If you look at the vuex docs: Mutations Follow Vue's Reactivity Rules and Vue Change Detection Caveats

This essentially means that Vue cannot detect changes applied for objects and arrays done in a specific way.

For objects: Typically when you add foreign keys that was not there on initialization

For Arrays: When you directly set an item with the index or change the length

Vue.use(Vuex);
const store = new Vuex.Store({
  modules: {
    products: {
      strict: true,
      namespaced: true,
      state: {
        step: 'first'
      },
      getters: {
        processingStep(state) {
          return state.step;
        }
      },
      mutations: {
        CHANGE_STEP(state) {
          state.step = 'last'
        }
      }
    }
  }
});

const demo = new Vue({
  el: '#demo',
  store: store,
  computed: {
    ...Vuex.mapGetters({
      processingStep: 'products/processingStep'
    })
  },
  methods: {
    changeStep() {
      this.$store.commit('products/CHANGE_STEP')
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/2.3.1/vuex.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.3/vue.js"></script>
<div id="demo">
  <p>processingStep: {{ processingStep }}</p>
  <button @click="changeStep">change Step</button>
  <div class="col" v-if="processingStep !=='last'">
    First step! :D
  </div>
  <p v-else-if="processingStep !== 'first'">
    This is the last..
  </p>
</div>
tony19
  • 125,647
  • 18
  • 229
  • 307
Sølve T.
  • 4,159
  • 1
  • 20
  • 31
  • The issue is that mutation is used inside the async action and action is getting called when you click. Also mutation can not work until we will not get response from API inside the action. –  Sep 04 '20 at 10:39
  • Can you provide this code or pseudocode in your question? – Sølve T. Sep 04 '20 at 11:00
  • 1
    @Dave Can you please edit your original question to refocus on what your actual issues are? From the very small amount of code we see we have tried to point you in the right direction in terms of how to watch Vuex state and subscribe to changes, but it seems like you are experiencing other deeper issues. – Ollie Sep 04 '20 at 12:53
  • I solved it with this marked answer: https://stackoverflow.com/questions/43270159/vue-js-2-how-to-watch-store-values-from-vuex –  Sep 04 '20 at 13:06
  • by using getter inside watch –  Sep 04 '20 at 13:07