So I've been investigating how mutating child props from the parent can be done. I already encountered the pitfall with using v-model. It throws this error:
[Vue warn]: 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. Prop being mutated: "number1"
I learnt to deal with this problem by emitting events from the child and using .sync modifier in the parent.
However, I still feel like I didnt really understand whats going on under the hood in both cases. As far as I understood, when mutating the childs properties from parent with v-model, the mutated data also mutates for any other parent importing the child.
To test this out, I built the following setup:
I have a child component with two inputs: One using v-model, the other an emitted event to mutate the childs props. We call it "compA":
<template>
<div>
<input
:value="number1"
@change="$emit('update:number1', $event.target.value)"
placeholder="number1emittedEvent"
/>
<input v-model="number1" placeholder="number1vmodel">
</div>
</template>
<script>
export default{
name: "compA",
props: {
number1: String
}
}
</script>
compA is being imported by compZ and compB. compZ also imports compB, and then compZ is finally being imported by myComplexView5.vue (my project uses routing).
compB:
<template>
<div>
<h1>compB containing compA</h1>
<compA/>
<p v-text="number1"></p>
</div>
</template>
<script>
import compA from "@/components/complexComponent5/compA.vue"
export default {
name: "compB",
components: {
compA
},
data(){
return {
number1:''
}
}
}
</script>
compZ:
<template>
<div>
<compA :number1.sync="number1"/>
<br>
<compB />
</div>
</template>
<script>
import compA from "@/components/complexComponent5/compA.vue"
import compB from "@/components/complexComponent5/compB.vue"
export default {
name: "compZ",
components: {
"compA" : compA,
"compB" : compB
},
data(){
return {
number1:''
}
}
}
</script>
myComplexView5.vue:
<template>
<div>
<h1>And More testing</h1>
<compZ />
</div>
</template>
<script>
import compZ from "@/components/complexComponent5/compZ.vue"
export default{
name: "myComplexView5",
components: {
compZ
}
}
</script>
Now, I expected the following behavior: When inputting into the inputfield which has v-model, I'd expect the paragraph inside compB to display the changes, since its text value is bound to the number1 prop from child compA. Since I mutated the childs props directly, the changed value should show up in any of the childs parents (or grandparents and so on).
But it doesnt. And it gets even better: When I use the inputfield with the event emitting from child to parent, the paragraph inside compB receives the changes! This is basically the opposite of what I've learned from the sync modifier docs:
In some cases, we may need “two-way binding” for a prop. Unfortunately, true two-way binding can create maintenance issues, because child components can mutate the parent without the source of that mutation being obvious in both the parent and the child.
That’s why instead, we recommend emitting events in the pattern of update:myPropName. For example, in a hypothetical component with a title prop, we could communicate the intent of assigning a new value with:
this.$emit('update:title', newTitle)Then the parent can listen to that event and update a local data property, if it wants to
Maybe this is caused by the data() inside compB which sets number1 to EMPTYSTRING when the component rerenders? I don't know, I'm very new to vue and I don't really understand when components rerender. I also used this
data() {
return {
number1: ''
}
}
inside compB To prevent this error from occuring:
[Vue warn]: Property or method is not defined on the instance but referenced during render.
I don't really know how else to prevent this error from occuring, since it seems that even though compB imported the prop from compA, the prop still needs to be declared in compB Oo
EDIT: I just found out that in the code I'm using, compZ still had this paragraph element:
<p v-text="number1"></p>
The string inputted into the inputfield appeared there, NOT in the paragraph element of compB. For some reason, even though I get no errors, neither through the emitted event from compA, nor through the v-model from compA does any change to the props inside compA show any impact inside compB... :(