I'm having trouble understanding the two-way binding between a parent and child component. So far, what I've read seems to recommend using prop.sync in conjunction with watching the prop. E.g:
Parent:
<child-component :childProp.sync="parentData"></child-component>
Child:
<template>
<input v-model="childData" @input="$emit('update:childProp', childData);"></input>
</template>
<script>
export default {
props: ['childProp'],
data() {
childData: childProp
},
watch: {
childProp(newValue) {
this.childData = newValue;
}
}
}
</script>
My problem with this is, wouldn't this create some sort of redundancy when either the parentData or the childData is updated? The flow would be thus (for parentData changed):
I'm assuming the cycle stops at step #5 because the updated value of the parentData is the same as the old value, so parentData doesn't really change, hence step #2 watcher is not triggered.
My problem with this is, if my reasoning is right, there will be some sort of redundancy in that changes to the parentData will go to the child and reflect back to itself, and vice versa. The reflection is the redundancy. Am I right so far? Or is my understanding of this completely off?
If I'm wrong, please help me understand where I went wrong. But if I'm right, then is there another way to achieve two-way binding without this redundancy?
I suppose you can simplify a child component code like this:
<template>
<input :value="childProp" @input="$emit('update:childProp', $event);"></input>
</template>
<script>
export default {
props: ['childProp']
}
</script>
There are several ways of achieving two-way bindings (in no particular order):
Here are some details to the methods that are available:
1.) Use props on components
Props should ideally only be used to pass data down into a component and events should pass data back up. This is the way the system was intended. (Use either v-model or sync modifier as "shorthands")
Props and events are easy to use and are the ideal way to solve most common problems.
Using props for two-way binding is not advised but possible, by passing an object or array you can change a property of that object and it will be observed in both child and parent without Vue printing a warning in the console.
The object will be reactive for both component and the parent:
I you pass an object/array as a prop, it's two-way syncing automatically - change data in the child, it is changed in the parent.
If you pass simple values (strings, numbers) via props, you have to explicitly use the .sync modifier
As quoted from --> https://stackoverflow.com/a/35723888/1087372
2.) Use v-model attribute
The v-model attribute is syntactic sugar that enables easy two-way binding between parent and child. It does the same thing as the sync modifier does only it uses a specific prop and a specific event for the binding
This:
<input v-model="searchText">
is the same as this:
<!-- VUE 2 -->
<input
:value="searchText"
@input="searchText = $event.target.value"
>
<!-- VUE3 -->
<input
:modelValue="searchText"
@update="searchText = $event.target.value"
>
if you are using the setup way of declaring your component, you can use the definemodel
function.
It takes care of setting up the prop and event for you under the hood.
const model = defineModel();
function inc() {
// emits "update:count" when mutated
count.value++;
}
3.) Use the sync modifier (for Vue 2.0)
The sync modifier is syntactic sugar just like v-model, only that the prop and event names can be set freely.
In the parent it can be used as follows:
<text-document v-bind:title.sync="doc.title"></text-document>
From the child an event can be emitted to notify the parent of any changes:
this.$emit('update:title', newTitle)
4.) Use v-model arguments (for Vue 3.0)
In Vue 3.x the sync modifier was removed.
Instead you can use v-model arguments which solve the same problem
<ChildComponent v-model:title="pageTitle" />
<!-- would be shorthand for: -->
<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
5.) Use Pinia (or Vuex)
As of now Pinia is the official recommended state manager/data store
Pinia is a store library for Vue, it allows you to share a state across components/pages.
By using the Pinia store it is easier to see the flow of data mutations and they are explicitly defined. By using the vue developer tools it is easy to debug and rollback changes that were made.
This approach needs a bit more boilerplate, but if used throughout a project it becomes a much cleaner way to define how changes are made and from where.
Take a look at their getting started section
If your project already uses Vuex, you can keep on using it.
Vuex 3 and 4 will still be maintained.
However, it's unlikely to add new functionalities to it.
Vuex and Pinia can be installed in the same project.
If you're migrating existing Vuex app to Pinia, it might be a suitable option. However, if you're planning to start a new project, we highly recommend using Pinia instead.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With