Vue 2 - Mutating Props Vue-Warn

Vue 2 - Mutating props vue-warn

This has to do with the fact that mutating a prop locally is considered an anti-pattern in Vue 2

What you should do now, in case you want to mutate a prop locally, is to declare a field in your data that uses the props value as its initial value and then mutate the copy:

Vue.component('task', {
template: '#task-template',
props: ['list'],
data: function () {
return {
mutableList: JSON.parse(this.list);
}
}
});

You can read more about this on Vue.js official guide


Note 1: Please note that you should not use the same name for your prop and data, i.e.:

data: function () { return { list: JSON.parse(this.list) } } // WRONG!!

Note 2: Since I feel there is some confusion regarding props and reactivity, I suggest you to have a look on this thread

Vue 2 Avoid mutating a prop directly since the value will be overwritten

All this is saying is that in your child component you should not mutate dataValue or dataValueOriginalCount directly, instead set a data property to those values and mutate your data property in your child component like this:

data() {
return {
originalCount: this.dataValueOriginalCount,
};
}

Now in your child component you can use originalCount and it will have whatever value dataValueOriginalCount had.

You can mutate dataValueOriginalCount in the parent component however you want since they are not a prop at that point.

Vuejs. Mutating prop in child component does not trigger warning. Wondering why

Why does the prop mutation propagate to the parent in one case but not in the other ? Does it have maybe something to do with how javascript stores these variable ?

Yes. JavaScript reference variables such as objects and arrays don't change their references when you mutate their properties/values. This means the parent can access the changed properties too, because it also has access to the reference. And Vue only gives a prop mutation warning if the reference changes.

Warning:

this.prop = {}

No warning:

this.prop.id = 5

Is it good practice to use that trick ?

No. It's back to violating One way data flow in which data is passed down through props (to child) and up through events (to parent).



Ok, but why doesn't Vue warn about those changes?

Consider an object with 1000 properties or an array with 1000 items. Vue would have to deep check all 1000 for changes, and there is no quick way to do it because each item has to be individually compared to the old value. This would have performance implications and probably make many apps unusable.


Here is a comment on the subject from Vue team member Bolerodan:

Generally you shouldnt do that. This can / could cause unexpected bugs in your code. Generally you should make a copy of your object, modify that, then emit upwards to the parent (as you have mentioned in your last paragraph I believe). This is the method I take and is the general consensus of top down, emit up approach.

How to solve Avoid mutating a prop in vue

There are two things here that go wrong:

  1. Changing/mutating the prop catProducts directly

Solution: emit an event like 'deleteProduct' to a parent components so that it can call axios.delete and then fetch a refreshed list of products and overwrite a list of products that is passed as a prop to a child component.


  1. Returning new product list from HTTP DELETE method.

Solution: call axios.delete and if it's successful then call axios.get to get a list of products and overwrite a list of products that is passed as a prop to a child component.

How to solve Avoid mutating a prop directly since the value will be overwritten

You can use .sync modifier, and update the prop with $emit :

<template>
<div @click="drawer = true" class="hamburger">
<span></span>
<span></span>
<span></span>
</div>

<mobile-drawer-menu :drawer.sync="drawer"/>
</template>

<script>
import MobileDrawerMenu from "./MobileDrawerMenu";

export default {
components : {
MobileDrawerMenu
},

data() {
return {
drawer : false,
}
},

}
</script>

Child

<template>
<div v-if="drawer" class="fixed top-0 left-0 z-200">
<div @click="$emit('update:drawer', false)" class="overlay"></div>
<div class="drawer-wrapper">
<div class="drawer-close">
<div class="flex items-center justify-end">
<svg @click="$emit('update:drawer', false)"></svg>
</div>
</div>
</div>
</div>
</template>

<script>
export default {
name: "MobileDrawerMenu",
props : {
drawer : {
type : Boolean,
required : true
}
},
}
</script>

JsFiddle: https://jsfiddle.net/hansfelix50/176oba9n/

Vue JS How to correct Avoid mutating a prop directly?

I'm not sure about your structure but you can get the same structure as below and it should work the same way, just send a false value to your parent.

Just send the data back to the parent ($emit from the child) and update the given prop in the parent ! More info on this pattern in the docs: https://v2.vuejs.org/v2/guide/components.html#Emitting-a-Value-With-an-Event

computed setters may also help but it's a bit more complex to understand so rather stick with a basic use-case. Same goes for the v-model syntax on components.


You need to have a props/emit (aka parent/child) relationship between your components like

Parent.vue

<template>
<div>
<child :name="name" @update-name="updateName"></child>
</div>
</template>

<script>
export default {
data() {
return {
name: 'bob',
}
},
methods: {
updateName(newName) {
this.name = newName
},
},
components: {
Child: () => import('./components/child'),
},
}
</script>

Child.vue

<template>
<div>
<p>{{ name }}</p>
<button @click="changeName">append nice</button>
</div>
</template>

<script>
export default {
props: ['name'],
methods: {
changeName() {
this.$emit('update-name', this.name + ' nice')
},
},
}
</script>

Also Michael Thiessen did a blog post on this: https://michaelnthiessen.com/avoid-mutating-prop-directly/

vuejs props Avoid mutating a prop directly

v-on:click="selectedPost = post" is the culprit; selectedPost is a prop here and you cannot assign to a prop.

There are two different solutions depending on what you want:

  1. Make selectedPost a local data property instead of a prop. You can then modify selectedPost but since it is no longer a prop, you cannot accept selectedPost from the parent anymore (but you're not really doing that anyway).
data() {
return {
selectedPost: null
}
}

  1. Instead of modifying selectedPost directly, you should emit an event which the parent can handle to update its data. The convention is to emit an event named like update:selectedPost which will make it work with the .sync modifier.

Change the click handler to:

@click="$emit('update:selectedPost', post)"

Then in the parent, update as follows:

<TableMenu
:posts="posts"
:selectedPost="selectedPost"
@update:selectedPost="selectedPost = $event"
></TableMenu>

Or you can use .sync to make the above a bit simpler (it does the same thing):

<TableMenu
:posts="posts"
:selectedPost.sync="selectedPost"
></TableMenu>

When i want to change prop in the setter avoid mutating prop error shows

you made wrong in setter,

 this.$emit('update:links', {...this.links = value})
  1. you have used this.links in left hand side to assign value, i mean you are directly trying to assign value into this.links so it is the cause of mutation error.

  2. you are trying to pass object into this.links instead of array though links props type is Array.

so try to resolve the above 2 issues, then i think it will work fine.

just replace the setter $emit like below,

 this.$emit('update:links', [...value])

or

this.$emit('update:links', value)

hope it will solve your issue.



Related Topics



Leave a reply



Submit