[Vuejs]-VueJS 3 How to trigger methods between two non-parent/child components

2👍

There are several ways to do it, and the implementation depends on you constraints.

First of all, vue3 no longer supports an event bus the way Vue2 did. That means that the event listening outside of what the components bubble up is not a library feature any more. Instead they recommend a 3rd party option

Vue docs link

In Vue 3, it is no longer possible to use these APIs to listen to a component’s own emitted events from within a component, there is no migration path for that use case.

But the eventHub pattern can be replaced by using an external library implementing the event emitter interface, for example mitt or tiny-emitter.

The bottom line is that it narrows your choice of three "strategies"

  • use 3rd party pub/sub library
  • pass the function itself
  • use a watch (or some part of reactivity) to execute a function.

The other part of the equation is the delivery method.

  • global/singleton
  • prototype/config
  • provide/inject

Global/Singleton

You can setup a file that will hold a singleton reference, which will allow any of the strategies.

create a pubsub instance, then you can listen and emit from anywhere

// pubsub.js
import mitt from 'mitt'
export const emitter = mitt();

Or if you want to just pass the function, you can wrap it, essentially creating a function that holds the instruction to execute another function.

// singleton.js
let updateComponentProxy = () => {};
export const setUpdateComponentProxy = (callback)=>updateComponentProxy=callback;
// component1
created(){
  setUpdateComponentProxy(()=>{this.updateComponent();})
}

// component2
// ..on some trigger
updateComponentProxy()

It’s a very ugly implementation, but it works, and maybe in some instances that’s appropriate

The 3rd option is using the reactivity. You can do this either by using vuex or a diy super paired-down version of it.

// mystore.js
import {ref} from 'vue';
export const updateComponentCount = ref(0);
export const pushUpdateComponentCount = () => updateComponentCount.value++;
// component1
import {watch} from 'vue';
import {updateComponentCount} from 'mystore.js';
created(){
  watch(updateComponentCount, this.updateComponent}
}

// component2
import {updateComponentCount} from 'mystore.js';
// ..on some trigger
pushUpdateComponentCount();

This will execute the updateComponent function when the value of updateComponentCount changes. You could do a similar thing with vuex, since it wouldnt (usually) be setup to run a function in the component, but provide some variable on the store that would trigger a change that you’d listen to. Also this example uses a counter, but you could even toggle that between true and false because it is not the value that’s important it’s the mutation.

Provide/Inject

If you are trying to pass information between non-direct child and parent, but part of the same "ancestry", the provide/inject feature.
enter image description here

This is meant to as a way to pass props without having to hand them along from parent to child, by just having it accessible to any child. You can then use with whichever strategy. There is however a caveat, which is that if you assign it to the root component, it is available to all components, which lends itself to some strategies better than others.

Config Object

If you assign something to a key on the app.config.globalProperties object, (where app is the root component instance) you can have that accessible from any of the child components. For example

import mitt from 'mitt';
const app = createApp(App)
app.config.globalProperties.emitter = mitt();

becomes accessible by

// component 1
created(){
  this.emitter.on('removeProduct', this.updateComponent())
}

// component 2

      removeProduct(product) {
        this.$root.selectedProducts.splice($.inArray(product, this.$root.selectedProducts), 1);
        this.emitter.emit('removeProduct')
      }

If you wanted to use in a vue3 setup() function though, you will need to access it using getCurrentInstance().appContext.config.globalProperties.emitter, since the component instance is not in the context.

👤Daniel

1👍

This is a common challenge in Vue, since the framework focuses on parent-to-child and child-to-parent data flows. You basically have two choices:

  1. Have ProductSelect $emit events up to a common parent, then pass props down to ProductCard.

  2. Create and import a global event bus.

You might also be interested in Vue’s own framework solution:

  1. Use Vuex to implement a central data source.

Leave a comment