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:
- 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.
- 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:
- Make
selectedPost
a local data property instead of a prop. You can then modifyselectedPost
but since it is no longer a prop, you cannot acceptselectedPost
from the parent anymore (but you're not really doing that anyway).
data() {
return {
selectedPost: null
}
}
- 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 likeupdate: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})
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.
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
Angular2 Router 2.0.0 Not Reloading Components When Same Url Loaded with Different Parameters
Replace Multiple Strings at Once
Is There an Equivalent of the _Nosuchmethod_ Feature for Properties, or a Way to Implement It in Js
Async Function Not Returning Value, But Console.Log() Does: How to Do
Js & Jquery Can't Detect HTML Elements, and Say's They Are Undefined
How to Convert a Float Number to a Whole Number in JavaScript
Why Is Arr = [] Faster Than Arr = New Array
Vue 2 - Mutating Props Vue-Warn
Sorting HTML Table with JavaScript
How to Detect the Screen Resolution with JavaScript
Why Is String Concatenation Faster Than Array Join
How to Get the Name of the Currently Running Function in JavaScript
Detect If Browser Tab Has Focus
Rendering Raw HTML with Reactjs
Does Every JavaScript Function Have to Return a Value