Search code examples
vue.jsvuexvue-composition-apivue-sfc

vue.js watching array state change in vuex using composition api and sfc


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.


Solution

  • 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)
    }