3
votes

I noticed something strange with prop mutation (Vue 2.6).

Mutating props directly in a child component should be avoided and will trigger the following famous warning

"Avoid mutating a prop directly since the value will be overwritten...."

So if in my parent component I have a data like

exemple: "Hello World"

that I passed down as a prop to a child component. If in that child component I do something like

this.exemple = "Hello World!"

I get a warning. Fair enough. Now I noticed that if the data in the parent is an object like

exemple : {message : "Hello World"}

and that in the child I do something like that

this.exemple.message = "Hello World!"

This does not trigger any warning and moreover the data in the parent component get updated

I'm wondering why. Why 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 store these variable ? Is it good practice to use that trick ?

3
You can emit the change this.$emit('update:example', 'Hello!') ... and in the parent sync it :example.sync="example" ... this way vue is aware of the change and can perform any needed reactive actionWashington Guedes

3 Answers

4
votes

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.

1
votes

A very simple answer is that You are mutating a reference value and Vue doesn't track the whole reference instead it only checks for the address where the reference is. So if you re-assign that value then it will warn you.

A good example to understand the behaviour is understanding through variable declaration const

const makes your variable immutable but again same applies to it if the value is primitive in nature you can't change it, but if it's a reference you can't change it but you can definitely update a value located at the reference

const a = 'Foo' // Primitive value

a = 'bar' // voilation
 
const b = { // Reference value
 name: 'Foo' 
}

b.name = 'Bar' // It's good

console.log(b)

b = {} // re-assign is voilation
0
votes

Because the mutation prevention only catches direct assignments to the prop - it does not catch manipulations of its properties. The idea is that if you assign new values to the prop - you are assigning to the prop and not to the variable that is sitting in the parent Vue component which feeds the prop.

As soon as something in the parent component updates the feeding variable - the prop will be updated with this new value and the value that you have assigned to the prop in your child component will be overwritten.

If you feed the prop with an object and then mutate a property inside the prop in your child component - it will affect the same object inside the parent component. But if you try to assign a new object to the prop in your child component - you should get the same warning about mutating props.