Communicating between Vue components
This article is part 3 of the series on Vue.js. Check out the other articles:
In the previous article we learned the basics of Vue components. Now that we know how to deal with them individually it's time to learn how to make them work together and communicate with each other.
Vue instance properties for communication - props and events
Before we dig into the patterns we should know our tools.
In general Vue components can communicate with themselves in two ways:
Let's start with the first one.
Props
We use props when we want to pass some data from the parent component to it's child.
Let's say we want to pass a message from parent (App) to child component (MessageBox) and display it in a paragraph.
Child component that receives the props needs to define it under props property which can be either Array or Object. For now let's stick with the Array format:
default// MessageBox.vue props: [‘message’]
This prop can be rendered in a template with mustache interpolation in the same way data and computed properties are:
default(kod) // MessageBox.vue <p> {{ message }} </p>
As you can see it's very simple and intuitive. Once we are ready to accept a prop we can pass it in the parent component the same way we are passing html attributes
default// App.vue <MessageBox message=”Hello World! />
In this case value passed to message prop will be treated as a string. Usually instead of plain strings we want to pass a variable or even a complex object.
The syntax is almost identical but instead of passing raw attribute we need to use v-bind directive (or its shorthand ":"). Assuming that we have a msg variable holding "Hello World!" string we can pass it like this:
default// App.vue <MessageBox :message=”msg” />
It's important to remember that passed prop should never be modified. Otherwise the data shared between components could desynchronize which can lead to many issues related to Vue reactivity system. It's a material for a whole new article and this knowledge is not essential right now so you should just remember to never mutate the prop. You can read more about this concept called one-way data flow here.
Here you can find working example of above code.
Events
Even though props are great for parent-child communication with one-way data flow sometimes we need a way to communicate with parent component. Let's have an example.
Imagine that we have a simple popup component. We are passing isPopupVisible variable from parent to isVisible property so the popup knows if it should display it's content or not. I ommited some of the implementation details to focus on the important parts.
default// PopUp.vue props: [‘isVisible’] // App.vue <PopUp :is-visible=”isPopupVisible” />
You probably noticed that I used kebab-case for isVisible property in a template. This process is called normalization and is needed because camelCase attributes are not valid HTML code. is-visible
property in a template is the same as isVisible
in the script.
Going back to the events. Let's think for a moment how we can handle showing and hiding popup component.
Showing is extremely simple. We just need to set isPopupVisible variable value to true. Things are getting a little bit more complicated when we try to find a way to close it.
As you know from previous paragraphs we can't modify isPopupVisible variable from the inside of the popup component. So how to tell the parent that it should change it's value to false?
This is a great use case for events. Just like the native HTML events we can either emit or listen to Vue event.
Every Vue component can emit the event that can be captured by it's parent. To emit an event we need to use $emit function with a name of the event. The method indicating that parent should set the value of isPopupVisible to false could look like this:
default// PopUp.vue methods: { close () { this.$emit(‘close’) } }
Now it can be captured and handled in the parent component the same way we dealt with native click event in the previous articles.
default// App.vue <PopUp :is-visible=”isPopupVisible” @close=”isPopupVisible = false” />
Once the close event is emitted by popup component we are setting isPopupVisible value to false.
Knowing this two properties we are now capable of creating real-world Vue applications from a set of components that can communicate with each other.
Here you can find working example of the above code.
Smart and dumb components
There is one concept related to parent-child communication that is considered a good practice in almost every Vue application and helps with keeping your state easy to maintain and debug.
Usually when we design complex applications we need to deal with a lot of data. This data is distributed across many components and the more of them we have the harder it is to remember where to look for some of the state properties. It's considered a good practice to have one wrapper component that fetches all the data and passes it down to the child components. This distinction between components responsible for getting the data and showing it is called "smart and dumb components".
As you probably have guesses smart components are responsible for fetching data in their methods and passing them via props to the dumb ones. Following this pattern we always have one container where all of the data is fetched which makes it much easier to reason about it.
Usually these containers are called pages. Each page (for example Home page, Post page on blog) is responsible for fetching data (like posts or comments) it needs and passing down to the dumb components that are responsible for displaying it.
Image loading...
Summary
In this article we learned how Vue components should communicate with each other.
- We use props when we want to pass data from parent to child component. Child component should never modify the data coming from it's props.
- We use events when we want to inform parent component about something happening inside child (for example ask to modify some data passed via props)
- To make Vue apps more maintainable you should use smart/dumb components distinction. Smart components are responsible for fetching data and passing it via props to dumb ones that have purely presentational role.