12

I have setup mitt and trying to dispatch event to another component but I am having hard time because in the setup() method it doesn't have this for accessing app instance.

Here is what I tried:

import App from './App.vue'
const el = document.getElementById('app')

import mitt from 'mitt';
const emitter = mitt();

const app = createApp(App)
app.config.globalProperties.emitter = emitter;
app.mount(el);

And in the component, I want to dispatch an event

export default {
   setup() {
      function toggleSidebar() {
          this.emitter.emit('toggle-sidebar');

          console.log(this); // binds to setup(), not the vue instance.
      }
   }
}

As this doesn't exist, I can't access the .emitter. What am I missing? How to use officially suggested mitt in Vue 3 composition api?


By the way if I use the v2 syntax, I can access this.emitter. But I am curious about Composition API way

export default {
  mounted() {
    console.log(this.emitter); // works
  }
} 
0

5 Answers 5

38

To use an event bus in Vue 3 Composition API, use Vue 3's new provide api in main.js, and then inject in any component:

1. Install mitt:

npm install mitt

2. Provide:

main.js

import { createApp } from 'vue';
import App from './App.vue';

import mitt from 'mitt';                  // Import mitt
const emitter = mitt();                   // Initialize mitt

const app = createApp(App);
app.provide('emitter', emitter);          // ✅ Provide as `emitter`
app.mount('#app');

3. Inject

3a. Any Component - Emit an event

import { inject } from 'vue'

export default {
  setup() {
    const emitter = inject('emitter'); // Inject `emitter`
    const mymethod = () => {
      emitter.emit('myevent', 100);
    };
    return {
      mymethod
    }
  }
}

Call mymethod from a button click or something.

3b. Any Component - Listen for the event

import { inject } from 'vue'

export default {
  setup() {
    const emitter = inject('emitter');   // Inject `emitter`

    emitter.on('myevent', (value) => {   // *Listen* for event
      console.log('myevent received!', `value: ${value}`);
    });
  },
}

Console

myevent received! value: 100 
Sign up to request clarification or add additional context in comments.

1 Comment

mitt is very useful. any decoupled scripts can be seamlessly coupled with vue3 component using mitt eventbus and this helped for a modular scripting with vue3. Thanks.
3

You may be able to use getCurrentInstance to get the global property

component:

import { getCurrentInstance } from 'vue';
export default {
  setup() {
    // get current instance
    const internalInstance = getCurrentInstance(); 
    // get the emitter from the instance
    const emitter = internalInstance.appContext.config.globalProperties.emitter;
  }
} 

2 Comments

This works. Just out of curiosity, is this the right way to use with mitt and alike packages with v3?
I believe so. it's the simplest way using the app instance, alternatively you can setup an external (global) emitter.
3

So far I have used this code to make the "emitter" available.

//main.ts
import mitt from 'mitt'
const emitter = mitt()
export default emitter

And then inside the components I use

import emitter from '@/main';

This worked so far in Vue2 and Vue3 - at least with the options API.

I have to admit though that I currently run into some trouble with the new vite server and the hot module reload (hmr). Is this style suboptimal in any way?

Comments

0

In order to use Event Bus for Composition API you can do the following:

Install and register mitt in your app.js like the TS did:

import App from './App.vue'
const el = document.getElementById('app')

import mitt from 'mitt';
const emitter = mitt();

const app = createApp(App)
app.config.globalProperties.emitter = emitter;
app.mount(el);

After that we'll move the logic for accessing global emitter to a separate file accessEmitter.js. I'll put it into js/Composables folder:

import { getCurrentInstance } from 'vue';

export default function accessEmitter() {
    return getCurrentInstance().appContext.config.globalProperties.emitter;
}

Now it can be utilized in your components:

<script setup>
import accessEmitter from '@/Composables/accessEmitter.js'
const emitter = accessEmitter();

// register listener
emitter.on("myEvent", (data) => {
    console.log('my event');
});
   
// remove listener
emitter.off("myEvent");

</script>

Comments

0

Solution by @Dan will work, however in the case of typescript you might get 'emitter' is of type 'unknown' error. Solution to this has been provided by @acantepie discussed here, the solution is to use singleton class like:

import mitt from "mitt";

export default mitt()

And use like:

import EventBus from "../lib/EventBus";
...

EventBus.emit(...)
EventBus.on(...)
...

Hope this helps someone like me :)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.