Vue.js displays warning when we try to change the prop value directly, like this:
Vue.component('Games', {
template: `
<div>
<ol>
<li v-for="game in games">{{ game }}</li>
</ol>
<button @click="clear">Clear games</button>
</div>
`,
props: ['games'],
methods: {
clear: function () {
this.games = [];
}
}
});
The warning being displayed is:
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value.
I know why that happens, but what I find surprising is that it doesn't happen with .push()
. If I change the method to add a value to the array instead of rewriting it, there's no warning:
methods: {
add: function () {
this.games.push('Whatever');
}
}
Why is there no warning? How is pushing directly to the prop fine and rewriting is not?
Error message: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value.
Props originate from a single data source, that is why it is by default reactive which means that every action taken on the original data source will affect every other child using that data source or prop, now imagine that your data could be changed midway, it wouldn't make for a very reliable data reference.
Use single file components if you can. If you come from React you can also find jsx for Vue useful, or even write render function by hand (though not recommended). If you want to totally nullify the time of the first rendering though, the only way to go is to do server-side rendering.
In summary, we use props to pass down data from the parent components to the child component(s). The child component also emit events to the parent component(s) in case you need to send data/events from the child to the parent component.
It's just because Array
is kind of reference memory. When you have Array
or Object
stored in any variable it's a reference variable.
Memory management
in such case, The reference variable will points to a memory address in heap
so you can add more n more value to address. However you cannot just replace that address with any new value even with the new address.
When a component is updated/rendered, the props are written as values to the underlying component model. This can happen again at "any" time. This will often happen with components rendered in v-if or v-for conditions. In this case the prop value is written again from the parent to the child.
If the child increases the counter it will only increase it's local copy of the prop, the original value parent.data.counter
will still remain at the value 5. If the parent is updated (for example by setting counter=counter-1
) then Vue will override the value in the child with the new value from the parent. So all changes of the child are lost - and this can lead to unexpected behaviour.
If the prop is an array, there exists only a single copy of this array in memory. Any mutation of the array will change the original array in parent as well as in child. This will only work for mutations of the array itself, if you would e.g. try child.propArray = child.propArray.slice(3)
you will have the same problems as before. Since slice does not change the original array, but creates a copy the child will have a different reference than the parent and unexpected behaviour will likely manifest.
Vue.component('child-comp', {
// camelCase in JavaScript
props: ['counter', 'arr'],
template: `<h3>Child Counter: {{ counter }} <button @click="inc">+</button>
-- Array: {{arr}} <button @click="push">push</button></h3></h3>`,
methods: { inc() { this.counter++ }, push() { this.arr.push(this.arr.length+1) } }
})
new Vue({
el: '#main',
data: { counter: 1, arr: [] },
methods: { inc() { this.counter++ }, push() { this.arr.push(this.arr.length+1) } }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<main id="main">
<h3>Main Counter: {{counter}} <button @click="inc">+</button>
-- Array: {{arr}} <button @click="push">push</button></h3>
<child-comp :counter="counter" :arr="arr"></child-comp>
<child-comp :counter="counter" :arr="arr"></child-comp>
</main>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With