Vue.js Developers
Published in

Vue.js Developers

Component Communication and State Management in Vue.js

One common problem when scaling an application with a modern framework is how do you handle the state and how do you share it across different components when needed.

There are currently 3 established patterns to solve this in Vue.js and you should choose one based on how complex your component architecture is. Let’s examine these options.

Props & Events

Passing props down and emit events up from one component to another. Let’s see the simple example of having two components that need to communicate.

The Vue.js way of doing this is passing a prop from component A to component B and emitting an event from component B.

Component A
Component B

So far so good. This is probably the cleanest and most performant way of doing this. But sooner or later our application will grow larger and more components will be added. Let’s examine the scenario below.

Component A and C that need to communicate remain the same but Component B that happens to be between them needs to carry the prop or event.

Component B

We only have three components and we have already added several lines of code to a component just because it happened to be between two components that needed to communicate.

Things are event worst when two siblings need to communicate. They need to do this using an ancestor which will receive the emitted event and pass it to the receiver with a prop.

Component B emits the event
Component A listens to the event and passes changed the prop
Component C gets updated prop

If you have a decent amount of components in your app you will soon have a common problem called Prop drilling with many components getting data that they don’t need just to forward them down the component tree.

The problem is even bigger when the components are siblings which means a combination of props and emit is needed for them to communicate. And what happens if one component changes place in the DOM? Then a refactor in all the components between them is needed.

This can easily get out of hand that’s why an event-driven architecture is most common.

Event Bus

Another solution that doesn’t rely on the component position in the DOM is using an event bus.

An event bus implements the publisher/subscriber pattern. It can be used to decouple the components of an application so that a component can react to events fired from another component without them having direct dependencies with each other. They only need to know the event bus.

Every subscriber can subscribe to a specific event. A subscriber will be notified when the event it subscribes to is published on the event bus. A publisher can publish events on the event bus when something happens.

Conveniently you can use a Vue instance for this. After importing Vue you can use it to listen and emit events like this:

Event bus initialization
A component that emits the event
A component that listens and reacts to the event

Using an event bus has the benefit of cleaner component architecture but in a large application is hard to track state changes because it’s not always obvious how the state was changed and which components reacted to that. In addition, we are using events to sync the state between components and we don’t have a centralized state.

State management Library (Vuex)

The third option and the one most commonly used with Vue is using the official Vuex library. Vuex is highly inspired by the flux methodology which redux also uses. It adds structure on how state changes, simplifies monitoring and enables features like time-travel and performance benchmarking. It serves as a centralized store for all the components in an application, with rules ensuring that the state can only be mutated in a predictable fashion.

In simple words, Vuex state is a global singleton object that every component can read or trigger actions to change it.

By defining and separating the concepts involved in state management and enforcing rules that maintain independence between views and states, we give our code more structure and maintainability.

Creating a Vuex Store

There are four components in every Vuex Store. State, Mutations, Actions, and Getters.

Store

State

Simply put State is a Javascript object that holds all the application data that needs to be shared across different components.

State

The main difference between the Vuex state and a simple javascript object is that the Vuex state is reactive and that you should not directly change this object. You have to do this through mutations.

Mutations

Mutations are simple functions that accept as first parameter the state and its the only way you should change the state.

Mutations

To invoke a mutation handler, you need to call store.commit

One important rule to remember is that mutation must be synchronous

Actions

When complex or asynchronous logic is required we shouldn’t do this inside a mutation. That’s why actions exist. Actions are very similar to Mutations with the difference that they can’t directly change the state and that you can have any type of asynchronous logic in them.

Actions

Actions can be dispatched from components or other actions like this

Getters

Sometimes you will need to manipulate the state. Components solve this problem with computed properties. Vuex getters work exactly the same. You can combine multiple state values or manipulate the data before sending the values to the component.

Getters

Modules

Due to using a single state tree, all state of our application is contained inside one big object. However, as our application grows in scale, the store can get really bloated.

To help with that, Vuex allows us to divide our store into modules. Each module can contain its own state, mutations, actions, getters, and even nested modules.

Modules declaration

Each module has its own local state but can have access to other module’s state through globalState and globalGetters that are part of the context passed automatically in every action or getter function. And of course, they can dispatch actions to other modules as well.

Namespacing

By default, actions, mutations, and getters inside modules are still registered under the global namespace. This allows multiple modules to react to the same mutation/action type.

If you want your modules to be more self-contained or reusable, you can mark it as namespaced with namespaced: true When the module is registered, all of its getters, actions, and mutations will be automatically namespaced based on the path the module is registered at.

Conclusion

Three different approaches for state management and the one you should use really depends on the scale of your application. Vuex is probably the most solid but adds boilerplate that might be unnecessary in small applications. So start with the simplest one and move to a more complex, once your application grows.

One important takeaway is that using one of the techniques above doesn’t necessarily prevent you from using the others in the same application. You can still pass data as props to a component if you don’t expect your hierarchy to change or an event bus for a stateless event-driven workflow. Know your tools and use them in your favor.

Additional resources:

--

--

--

Helping web professionals up their skill and knowledge of Vue.js

Recommended from Medium

Why do we need to bind, call and apply in Javascript

5 Strange Reasons Why Developers Got Fired From The Job

JavaScript Important Ten Interview Question.

REACT JS | Introduction

Angular Vs React Js — The Pros and Cons

JavaScript Problem Solvers: Unique Morse Code Words

//platform.twitter.com/widgets.js from Twitter https://twitter.com/preferredpcare

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Fotis Adamakis

Fotis Adamakis

Front End Engineer @ Glovo // Vue.js Athens Meetup Coorganizer

More from Medium

People You Should Follow If You Are Working With Vue

Vue3 Tailwind CSS Form Components Part III — Reusable BaseCheckbox Component

Create a Basic useFetch Hook in Vue.js

vue custom useFetch hook

Installing the Vue i18n CLI plugin for Vue 3 — in a mono repo