I need to pass data from child to parent(nested) component, I know we can do this with emit
or store
but I want to know, Is any other way, for doing this in a better manner?
I don't want to use emit
just because my parent component is 6 levels up.
Thank you.
CodePudding user response:
you can use eventBus for this purpose. Here is a working demo.
CodePudding user response:
Store doesn't necessary need to be vuex
or pinia
. It can be an external reactive()
, imported in all the components which use it, regardless of their relation.
Think of it as a data: () => ({})
object, but without the component. Here's an example where I'm creating a simple reactive "store", and injecting it in both App and Child (6 levels deep).
const { createApp, reactive, defineComponent } = Vue;
const store = reactive({
foo: 'bar'
})
const app = createApp({
setup: () => ({ store })
});
app.component('parent', defineComponent({
template: '<div >Parent<br><slot></slot></div>'
}));
app.component('child', defineComponent({
setup: () => ({ store }),
template: '<input v-model="store.foo">'
}));
app.mount('#app')
.parent .parent {
padding-left: 1rem;
}
input {
margin-left: 1rem;
}
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<div id="app">
<parent>
<parent>
<parent>
<parent>
<parent>
<child />
</parent>
</parent>
</parent>
</parent>
</parent>
<pre v-text="store" />
</div>
Same thing, in Vue2:
const store = Vue.observable({
foo: 'bar'
})
Vue.component('parent', {
template: '<div >Parent<br><slot /></div>'
});
Vue.component('child', {
computed: {
store: () => store
},
template: '<input v-model="store.foo">'
});
new Vue({
computed: {
store: () => store
}
}).$mount('#app')
.parent .parent {
padding-left: 1rem;
}
input {
margin-left: 1rem;
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
<div id="app">
<parent>
<parent>
<parent>
<parent>
<parent>
<child />
</parent>
</parent>
</parent>
</parent>
</parent>
<pre v-text="store" />
</div>
In a real app, you would have the "store" defined in a file and imported in any component using it. (e.g:
import { reactive } from 'vue'
export const myStore = reactive({
//...stuff
})
anywhere else:
import { myStore } from './path/to/store'
Note: If you're using Vue2, you can import reactive
from @vue/composition-api
plugin or you can use Vue.observable()
instead. It provides the same functionality.
Even if the above works, the more you use it, the more you would actually benefit from using pinia
or vuex
, as they come with the great benefit of being integrated with vue devtools. giving you the ability to inspect, roll back or replay changes to your reactive state. Not to mention you can also define actions on the store, so you don't have to scatter them around multiple components, especially useful when you want to perform the same action from two or more different components.
Another option is to use provide/inject developed specifically for bypassing the parent/child chain. It's useful when you want to provide specifically for the descendants of the current component, but not to descendants of its siblings. For example:
item-1
component
component
child-1
item-2
component
component
child-2
child-3
In the above case, if you want child-2
and child-3
to inherit/update item-2
but not affect item-1
, then you would use provide
in item-2
and inject
in child-2
and child-3
.
I personally haven't used inject/provide, as I find working with external stores quite powerful. Even when, in theory, I have a case like the above. I use one single store where I uniquely identify ancestors. The grand children use that identifier to update the appropriate ancestor in the store.
In fact, the only place where i use emit
is in agnostic components, which can have any parent, take some input, perform some action and then update their parent (e.g: the context). A good such example are generic form components which don't have to know anything about the parent form.
Another such example are agonstic list renderers, which, again, don't care what they list. They "emit" the click action to the parent component which knows what that click actually means.