I observed that when a normal property that came from data()
and a computed property that is derived from it are passed through an event, the former keeps its reactivity while the latter loses it.
I set up the following test case for it (also as a JSFiddle if you prefer that):
const EventBus = new Vue();
Vue.component('comp', {
data() {
return {
arrData: ['a', 'b']
};
},
computed: {
arrComputed() {
return this.arrData.map((e) => e.toUpperCase());
}
},
template: `
<div>
<div>Original array: {{ arrData }}</div>
<div>Computed array: {{ arrComputed }}</div>
<button @click="remove('a')">Remove a</button>
<button @click="remove('b')">Remove b</button>
<button @click="pass">Pass through event</button>
<button @click="reset">Start over soft</button>
<button @click="resetHard">Start over hard</button>
</div>`,
methods: {
remove(name) {
name = name.toLowerCase();
if(this.arrData.indexOf(name) != -1) {
this.$delete(this.arrData, this.arrData.indexOf(name));
}
},
pass() {
EventBus.$emit('pass', this.arrData, this.arrComputed);
},
reset() {
this.$set(this.arrData, 0, 'a');
this.$set(this.arrData, 1, 'b');
},
resetHard() {
this.arrData = ['a','b'];
}
}
});
Vue.component('othercomp', {
data() {
return {
items1: [],
items2: []
}
},
mounted() {
EventBus.$on('pass', this.receive);
},
template: `
<div>
<div>Original array: {{items1}}</div>
<div>Computed array: {{items2}}</div>
</div>`,
methods: {
receive(items1, items2) {
this.items1 = items1;
this.items2 = items2;
}
}
});
var app = new Vue({
el: '#app',
components:['comp', 'othercomp']
})
<script src="//unpkg.com/vue@latest/dist/vue.js"></script>
<div id="app">
<comp></comp>
<othercomp></othercomp>
</div>
How is a computed different from a normal property so that this difference in behaviour occurs?
I learned from a previous question that passing reactive objects around like this is bad practice and that I should use a getter function instead, however I'd still like to know why this difference occurs.
I would not expect this to work. By returning the results of
map()
you are passing a copy of the reactive array to the other component. This copy will not react to changes of the original array — it's a totally different array. And even though the computed property will update whenarrData
changes, it won't automatically send another event to the second component telling it to update it's data array. I would think you'll need a computed property on both components — you could do this in a DRY fashion with a mixin or filter (which is probably more appropriate in this case).You could fire the event in the computed function. But this seems pretty hacky to me:
An object variable (an Array is an object) contains a reference (or handle) to the object. When you assign one object variable to another, the handle is copied, and both variables are handles to one object. Object operations on one will be "seen" by the other.
So after
both
foo
andbar
, since they refer to the same object, would be[1, 2]
. Passing values works the same way, but it's easier to illustrate with simple assignments.It should be clear that
assigns a new handle to
foo
, so it no longer references the same array thatbar
does.In your example,
arrComputed
creates a new Array object every timearrData
changes. So when you passarrComputed
, you are passing the handle it created the last time it ran. WhenarrData
changes,arrComputed
creates a new Array object, but the recipient of the old handle doesn't get that, and the Array associated with its handle is never updated.