Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Vue.js allow pushing to prop array?

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?

like image 473
Robo Robok Avatar asked Jun 14 '19 10:06

Robo Robok


People also ask

How do I stop mutate props in Vue?

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.

Why are props immutable in Vue?

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.

How can you prevent layout jumps in VUE JS?

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.

Why we use props in VUE JS?

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.


2 Answers

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.

like image 128
Satyam Pathak Avatar answered Nov 15 '22 00:11

Satyam Pathak


As the warning says, one results in unexpected behaviour

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.

This problem will not happen, if the prop is a reference, which is mutated

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.

Here is a code-fiddle demonstrating the issues:

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>
like image 36
Falco Avatar answered Nov 14 '22 22:11

Falco