1

I am trying to watch for changes in a state in vuex. The state is an array. I am changing the values of that array with the press of a button. Whenever i press that button and the array (vuex state) changes i want to display the values in a list on the screen.

This is the main vuex store:

import { createStore } from 'vuex';
import rollingModule from './modules/rolling/index.js';

const store = createStore({
  modules: {
    rolling: rollingModule,
  },
});

export default store;

This is my vuex store module:

export default {
  namespaced: true,
  state() {
    return {
      numbers: [0, 0, 0, 0, 0],
    };
  },
  mutations: {
    rollDice(state, payload) {
      state.numbers = payload;
    },
  },
  actions: {
    rollDice(context) {
      const rolledNumbers = [];
      for (let i = 0; i < 5; i++) {
        rolledNumbers.push(Math.floor(Math.random() * 7));
      }
      context.commit('rollDice', rolledNumbers);
    },
  },
  getters: {
    getNumbers: (state) => state.numbers,
  },
};

My first attempt was using a computed property to react to changes but that doesnt seem to be working. I have then added a watcher for that computed property to console.log the old and new values but the watcher seems to never get fired.

Heres my component code:

<template>
    <ul>
      <li v-for="number in rolledNumbers" :key="number">
        {{ number }}
      </li>
    </ul>
</template>

<script setup>
import { computed, watch } from 'vue';
import { useStore } from 'vuex';

const store = useStore();

const rolledNumbers = computed(() => {
  store.getters['rolling/getNumbers'];
});

watch(rolledNumbers, (newValue, oldValue) => {
  console.log('Old Array: ' + oldValue);
  console.log('New Array: ' + newValue);
});
</script>

I've read something about deep watchers to watch for changes to the values of the arrays but i couldn't find anything good for the composition api and .

EDIT 1: My watcher now fires when the nested elements change. This is the code for that:

watch(
  rolledNumbers,
  (newValue, oldValue) => {
    console.log('Old Array: ' + oldValue);
    console.log('New Array: ' + newValue);
  },
  { deep: true }
);

Unfortunately oldValue and newValue both return undefined.

1 Answer 1

1

Your mutation is replacing the array in numbers with a completely new array. This means that the reference to the array is lost, breaking the reactivity:

rollDice(state, payload) {
  state.numbers = payload;
}

You need to replace the contents of the array in order for the references to be preserved. You could do something like:

rollDice(state, payload) {
  # Remove all items from the array
  state.numbers.length = 0
  # Fill the array with the items from the payload array
  [].push.apply(state.numbers, payload)
}
Sign up to request clarification or add additional context in comments.

6 Comments

first of all thank you for the quick reply. i was thinking something similar and tried various approaches to replace the contents of the array including your exact code but now the watcher does not get triggered anymore
This might be a red herring, but you probably want an explicit return in the `computed property, for certainty if nothing else.
(failing that it might be good to put it in a codepen to make it easier for others to debug)
The explicit 'return' did actually fix it. The watcher is not even needed anymore. Thank you so much!
Great! You probably want to consider enabling the eslint vue plugins, since they'll flag up simple issues like that one: eslint.vuejs.org/rules/return-in-computed-property.html
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.