Vue.js: why event bus is bad idea

In February 2019 I wrote a post about global event bus in Vue.js. It’s a pattern to achieve very common thing: transform data not only from parent to child components, but also in reverse order. Event bus allows us to also emit events from parent to children and simply use methods for them. Unfortunately, because of many reasons, it isn’t good pattern and we should avoid it, if there are more possible options to use. After year and a lot of new experiences I can say: event bus is antipattern and should be avoided.  Why? In this short post, I will try to explain this in the simplest way I can. Of course, I open for discussion about that, so feel free to comment and write about your experiences.

For reminder, how can we use event bus and how it works: we must create new Vue instance in separate file and export it. Then, we can import this file in many components and declare our events using $emit and $on methods. There is very simple example:

// eventBus.js 
import Vue from 'vue'
export const eventBus = new Vue()


// First component
import {eventBus} from '@/global/eventBus'
(…)
methods: {
    myMethod (data) {
        // Do something
        eventBus.$emit('my-global-event', data)
    }
}
 

// Second component
import {eventBus} from '@/global/eventBus'
(…)

// Or created, or beforeMount
mounted () {
    eventBus.$on('my-global-event', this.actionForGlobalEvent)
}

It’s the simplest example, but it will work: we create new event bus (new Vue instance), import it in our component and set emits and actions. It’s simple, clear and very easy to read. So, why is it a bad option?

Event bus issues

First, naming. One event bus instanton is not separated by any namespace, so, it’s easy to have many events with the same name. Of course, we can create many instances, for example one for components group, but it will be a bigger problem when our application grows. We can also establish naming convention used in app, for example events: mycomponent/myEvent or mycomponent_myevent etc. Yes, it will work, but we must always remember about that, our whole team must remember about that. But it isn’t the most important reason, why I don’t like event bus now.

The biggest problem are events, $on declarations. We will use them but… what about $off? We also must remember about that and use it on our component beforeDestroy methods. Why? Because without that, methods binding to event bus events will be called even if we destroy component! It’s ok, because we bind them using… yes, global event bus, they still exist inside it and will work. So, we must always remember about removing them from code – and when our app grows, it will be a terrible issue and terrible thing to debug and fixing.

How can it cause problems in real usage? I will describe from my experience: there is parent component List, which display many child elements. We can click each element to change route / move to child details and go back to list. Both use event bus to send some data / event in two-directional way. Also, child component includes sub-child component with method, which emit global event – this event is handled by List (so root parent). And the issue: if we forgot about using $off and then go to one child, go back to list, go to second child, go back to list, go to third child and call method with global event… List will receive not one, but three emits with different data! In many cases, this may indicate incorrect data.

Use Vuex instead of event bus

Yeah, you can just remember about using $off but… you have only illusionary control about that, and it’s very easy to lose it. What can I recommend? The best option is to use Vuex instead of event bus. It provides namespacing, so there is no problem with many similar or the same methods. It has clear, defined state, so we know, what can happen in our application. It provides clear dispatches, actions and commits, data from it is in computed, so we will see changes without any issues. If we remove/destroy component, it will not use any methods after changes in Vuex: one exception is to push function inside Vuex, but, you must do it directly. In summary: Vuex is clear. Is also simple, but give you better control.