How to notify child component in Vue 3 using defineExpose and defineEmits
Inter-component communication is one of the features that tools like Vue provides. At the simplest level, inter-component communication is about leveraging the affordances of the library to make two components to talk to each other. Whether it's a modal window or a complex form, getting components to communicate effectively is crucial for a seamless user experience. In this article, I'll explain how a parent component can nudge a child component to behave differently in response to a user action or to other changes in application state. For this, we'll consider two approaches: sharing a variable with the parent using defineExpose
and notifying the parent about an event using defineEmits
.
Imagine you're building a simple e-commerce application with a product list and a shopping cart. When a user clicks the "Add to Cart" button, you want to open a modal window to confirm the addition. The modal window is a child component, and the product list is the parent component. How do you get the child component to open when the button is clicked? By the end of this article, you'll learn how to use defineExpose
and defineEmits
to facilitate communication between components.
We'll cover two main approaches to achieve this communication. First, we'll look at how to share a variable with the parent using defineExpose
. This method allows the parent component to access the child component's properties and functions directly. Second, we'll discuss how to notify the parent about an event using defineEmits
. This approach enables the child component to emit custom events that the parent component can listen to and respond accordingly. By understanding these two techniques, you'll be able to design more robust and maintainable component interactions in your Vue applications.
Sharing a Variable with the Parent
The defineExpose
method in Vue 3 allows a child component to expose its properties and functions to the parent component. This way, the parent can access and manipulate the child's state directly, like so:
// ChildComponent.vue <template> <div v-if="isOpen">Modal is open!</div> </template> <script setup> import { ref, defineExpose } from 'vue'; const isOpen = ref(false); defineExpose({ isOpen }); </script>
// ParentComponent.vue <template> <button @click="openModal">Open Modal</button> <ChildComponent ref="child" /> </template> <script setup> import { ref } from 'vue'; import ChildComponent from './ChildComponent.vue'; const child = ref(null); const openModal = () => { if (child.value) { child.value.isOpen = true; } }; </script>
In this example, the ChildComponent
exposes its isOpen
property using defineExpose
. The ParentComponent
can then access this property using the ref
attribute and toggle the modal's visibility.
Notifying the Parent about an Event
While sharing a variable with the parent can be useful, it's not always the desired approach. Sometimes, you want the child component to notify the parent about an event without exposing its internal state. This is where defineEmits
comes handy, as it allows a child component to emit custom events that the parent component can listen to. Here's an example:
// ChildComponent.vue <template> <div v-if="isOpen">Modal is open!</div> <button @click="closeModal">Close</button> </template> <script setup> import { ref, defineEmits } from 'vue'; const isOpen = ref(false); const emit = defineEmits(['opened', 'closed']); const openModal = () => { isOpen.value = true; emit('opened'); }; const closeModal = () => { isOpen.value = false; emit('closed'); }; </script>
// ParentComponent.vue <template> <button @click="openModal">Open Modal</button> <ChildComponent @opened="onOpened" @closed="onClosed" /> </template> <script setup> const onOpened = () => { console.log('Modal opened!'); }; const onClosed = () => { console.log('Modal closed!'); }; const openModal = () => { console.log('Just do it ✔︎'); }; </script>
In this example, the ChildComponent
emits opened
and closed
events when the modal is toggled. The ParentComponent
listens to these events using the @opened
and @closed
attributes and responds accordingly.
Communicating between components is very important in building robust and interactive Vue applications. By using defineExpose
and defineEmits
, you can create maintainable component interactions that enhance user experience. While I prefer using defineEmits
for its decoupling benefits, I still use defineExpose
sometimes when you I to access a child component's properties directly for whatever reason. Choose the approach that best fits your use case. Hope this was any combination of being informative and fun to read!