Search code examples
javascriptvue.jsvuejs2vue-reactivity

Vue reactivity issue, need some explanations


I've removed most useless parts of my code, so don't worry if this code doesn't really make sense, it's just to show you what's not working.

First, I create an array from a base array called objects:

objects: [
        {
          text: "I dont trigger stuff",
        },
        {
          "text": "I dont trigger stuff",
        },
        {
          text:"I trigger stuff",
          activated: undefined,
        },
],

And the create function

created() {
    const newArray = [];

    this.objects.forEach(anObj => {
      anObj.activated = false;
      newArray.push(anObj);
    });

    this.filteredObjects = newArray;
},

I initialize a property activated to false. In my real code I'm not using a forEach, but a find but the result is the same.

Then, I display a few buttons to trigger an "activation"

<button
    v-for="(myObj, index) in filteredObjects"
    :key="index"
    @click="activateObject(myObj, index)">
    {{ myObj.text }}
</button>

And the function being triggered is this one:

activateObject(anObj, anObjIndex) {
      this.$set(this.filteredObjects[anObjIndex], 'activated', !anObj.activated)
},

My goal here is just to update the activated property.

To check if reactivity is working, I've got a watcher:

watch: {
    filteredObjects: {
      handler() {
          alert('called')
      },
      deep: true,
    }
},

I've got two questions:

1/ Since all activated properties are set to false for all objects, why is there only one working, the one with the property initially set to undefined?

2/ If I update my activation function to:

activateObject(anObj, anObjIndex) {
      anObj.activated = !anObj.activated;
      this.$set(this.filteredObjects, anObjIndex, anObj);
},

It works well. Can someone explain me why, and what is the difference?

In both cases, VueJS Devtools shows the updated values when clicking on refresh. It's a reactivity issue.

You can find a fiddle here:

https://jsfiddle.net/dv1jgneb/


Solution

  • From the Docs:

    Since Vue performs the getter/setter conversion process during instance initialization, a property must be present in the data object in order for Vue to convert it and make it reactive.

    This explains why only the third button "trigger stuff".

    So, you may either add that attribute in the data(), or as said in the docs, use this.$set:

    this.objects.forEach(anObj => {
      this.$set(anObj, 'activated', false);
      newArray.push(anObj);
    });
    

    JS Fiddle

    Hope this helps!