Blog>
Snippets

Cross-Component Event Handling with Provide/Inject

Illustrate how to use provide and inject to share a common event bus or an event handling function among unrelated components in a Vue.js 3 app.
// EventBus.js - an event bus component using provide/inject pattern
import { reactive, readonly } from 'vue';

export const EventBus = {
  install(app) {
    const eventBus = reactive({});

    app.provide('eventBus', readonly(eventBus));

    app.config.globalProperties.$on = (event, callback) => {
      if (!eventBus[event]) eventBus[event] = [];
      eventBus[event].push(callback);
    };

    app.config.globalProperties.$off = (event, callback) => {
      if (!eventBus[event]) return;
      const index = eventBus[event].indexOf(callback);
      if (index > -1) eventBus[event].splice(index, 1);
    };

    app.config.globalProperties.$emit = (event, ...args) => {
      if (!eventBus[event]) return;
      eventBus[event].forEach(callback => callback(...args));
    };
  }
};
This is a plugin definition for an EventBus. By calling app.provide, it makes an object named 'eventBus' available for injection in all child components. It also extends Vue with `$on`, `$off`, and `$emit` methods to subscribe to events, unsubscribe from events, and emit events, respectively.
// main.js - where we install the EventBus plugin
import { createApp } from 'vue';
import App from './App.vue';
import { EventBus } from './EventBus.js';

const app = createApp(App);
app.use(EventBus);
app.mount('#app');
This code is to be included in the main startup file of a Vue.js application. The EventBus plugin is imported and then installed into the Vue application instance, enabling the provide/inject pattern for cross-component event handling.
// ChildComponent.vue - a Vue component that injects the event bus
<template>
  <button @click="emitEvent">Emit an event</button>
</template>

<script>
import { inject } from 'vue';

export default {
  setup() {
    const eventBus = inject('eventBus');

    const emitEvent = () => {
      this.$emit('myEvent', 'Hello from ChildComponent!');
    };

    return {
      emitEvent
    };
  }
};
</script>
This code is for a child component that needs to emit an event through the EventBus. The event bus is injected into the component's setup function and then used to emit an event when a button is clicked.
// AnotherComponent.vue - a Vue component that listens for events
<template>
  <!-- Component template here -->
</template>

<script>
import { onMounted, onUnmounted } from 'vue';

export default {
  setup() {
    onMounted(() => {
      this.$on('myEvent', (message) => {
        console.log('Event received:', message);
      });
    });

    onUnmounted(() => {
      this.$off('myEvent');
    });
  }
};
</script>
This code is for a different component in the application that needs to listen for events emitted from other components. It uses the event bus to register an event listener when the component is mounted and to unregister it when the component is unmounted.