1👍
From the docs
Vue cannot detect property addition or deletion.
and
…it’s possible to add reactive properties to a nested object using the Vue.set(object, propertyName, value) method
and
Vue cannot detect the following changes to an array:
- When you directly set an item with the index, e.g. vm.items[indexOfItem] = newValue
- When you modify the length of the array, e.g. vm.items.length = newLength
But it says nothing about mutating an existing object[property]
.
-
In the first example, an existing object property is being mutated, so it works. (The
age
property of the first item of thepeople
array.) -
In the second example, an array item is being set directly by index, so it doesn’t work. (The
0
index of thepeople
array.)
This can seem counter-intuitive because a previously set array index can’t be modified by index, but a previously set object property can.
I’m guessing that the reason for this has a lot to do with Object.defineProperty
and Vue’s reactivity process.
(Confused me at first, too.)
0👍
It’s a caveat about Vue.js reactivity.
You should use Vue.set
if you want to change link to object.
For example it will work:
changePeopleNotWorking(state) {
const newPeople = [...state.people];
newPeople[0].age = newPeople[0].age + 1;
Vue.set(state, "people", newPeople);
}
https://codesandbox.io/s/dreamy-cdn-x90sk?file=/src/main.js
UPD:
From Docs:
Vue cannot detect the following changes to an array:
When you directly set an item with the index, e.g. vm.items[indexOfItem] = newValue
When you modify the length of the array, e.g. vm.items.length = newLength
var vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})
vm.items[1] = 'x' // is NOT reactive
vm.items.length = 2 // is NOT reactive
So, using an array you cannot change item by index (even existing item), because it’s not reactive.
But you can use
Vue.set(vm.items, indexOfItem, newValue)
For example:
Vue.set(state.people, 0, { age: state.people[0}.age + 1} )
UPD 2.
1
and 2
they are not the same. In the first example we don’t re-assign array item by link, instead this we change property of observable object. If you look at state in console.log
you will see, that objects in array (which were initialized) are observable. So, Vue is able to watch changes there.
0👍
Properties of plain objects are reactive (defined as getter/setter pair), while array indexes aren’t:
This assignment of array element results in non-reactive plain object:
people[0] = { age: people[0].age + 1 }
This is the limitation of Vue reactivity for arrays:
Vue cannot detect the following changes to an array:
- When you directly set an item with the index, e.g.
vm.items[indexOfItem] = newValue
- When you modify the length of the array, e.g.
vm.items.length = newLength
It can be addressed with Vue.set
or array splice
, as the documentation suggests:
Vue.set(people, 0, { age: people[0].age + 1 });
// or
people.splice(0, 1, { age: people[0].age + 1 });
Or with immutable array, which is acceptable for non-critical places:
let [person, ...people] = state.people;
state.people = [{ age: person.age + 1}, ...people];