'Vuejs/Nuxtjs watch is unable to watch for changes within the Array of Objects from Vuex Store

I am developing an application using Vuejs/Nuxtjs within this application I have a component IdentifiersNode.vue within that I would like to watch for a Vuex Store array identifiersArray.

I am able to watch the identifiersArray for direct changes such as push, direct key-value changes but when I add a new object to the object within the identifiersArray then watch would not work for some reason.

Following is the watch function in my IdentifiersNode.vue:

watch: {
    '$store.state.modules.identifiersInfoStore.identifiersArray': {
        handler (val) {
            console.log('WATCH : ' + JSON.stringify(val, null, 4))
        },
        deep: true
    }
},

Following are the changes happening to my identifiersArray within the Vuex Store Mutations present in identifiersInfoStore .js:

saveIdentifiersInfo (state, payload) {
    // Upon saving the Identifiers info save the information into respective object of identifiersArray
    const identifiersNode = state.identifiersArray.find(node => node.identifiersId === state.currentNodeId)

    console.log('NODE BEFORE : ' + JSON.stringify(identifiersNode, null, 4))
    
    identifiersNode.instanceData = payload.instanceData
    
    console.log('NODE ARRAY AFTER : ' + JSON.stringify(state.identifiersArray, null, 4))
},

As we can see I am adding an object to an existing object within the identifiersArray but when I do this I would expect my watch function to trigger in IdentifiersNode.vue as I have deep: true but for some reason, it does not work at all.

Can someone please tell me if it's possible to detect even a minute change to an array using the Vuex store and Vue watch? If not then what is an alternative that I can take?

Following is my identifiersNode from console.log:

NODE BEFORE : {
    "identifiersId": 1,
    "name": "identifiersNode1"
}

Following is the state.identifiersArray from console.log:

NODE ARRAY AFTER : [
    {
        "identifiersId": 1,
        "name": "identifiersNode1",
        "instanceData": {
            "name": "Batman",
            "job":"IT"
        }
    }
]


Solution 1:[1]

Providing the answer can be helpful to someone else in the future.

I was actually appending the object to an existing object in Array within Vuex Store. Hence, the watch was not able to identify the change. I changed it to vue.set which worked for me.

Previously I was using to append to my existing object:

identifiersNode.instanceData = payload.instanceData

Later I changed to:

// Use the Vue reactive approach to add the instanceData object to existing object
Vue.set(identifiersNode, 'instanceData', payload.instanceData)

Following is my watch in Vue component, I used this code within my mounted can also be used within watch as well:

// Watch for the changes on the identifiers array 
this.$watch('$store.state.modules.identifiersInfoStore.identifiersArray', (val) => {
    console.log(JSON.stringify(val,null,4))
}, { immediate: true, deep: true })

Solution 2:[2]

If you are using vuex, I would recommand using mapGetters to get your items from the store. This will automatically watch the values and re-render each time your items changes.

Here is the documentation : documentation

Little example

Store

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

const state = {
  data: ["Hello", "world"]
};

const mutations = {
  addItem(state, item) {
    state.data.push(item);
  }
};

const getters = {
  getData: (state) => state.data
};

export default new Vuex.Store({
  state,
  mutations,
  actions,
  getters
})

Component

<template>
  <div>
    <h2>{{ data }}</h2>

    <input type="text" v-model="inputValue" />
    <button @click="addItemToStore">add item</button>
  </div>
</template>

<script>
import { mapGetters, mapMutations } from "vuex";
export default {
  name: "HelloWorld",
  computed: {
    ...mapGetters({
      data: "getData",
    }),
  },

  data: () => {
    return {
      inputValue: "",
    };
  },

  methods: {
    addItemToStore() {
      this.addItem(this.inputValue);
      this.inputValue = "";
    },
    ...mapMutations({
      addItem: "addItem",
    }),
  },
};
</script>

Here is a codepen as an example : https://codesandbox.io/s/vuex-store-forked-bl9rk0?file=/src/components/HelloWorld.vue

If you want to change an object property (Or in your case addind a new object to an array which is a property of your object) the best thing to do is completely re-push the current object.

for example if you have an array like [{id: 1, values: [1,2,3]}, {id: 2, values: [4,5,6]}] you can update values with the js splice method :

addValue(state, {id, valueToAdd}){
   const pos = state.arr.findIndex(x => x.id === id)
   if (pos !== -1){
      const newObj = {...state.arr[pos]}
      newObj.values.push(value)
      state.arr.splice(pos, 1, newObj)
   } 
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 BATMAN_2008
Solution 2