How to fire an $emit event from Vue Composable

11.5k Views Asked by At

I have a vue composable that needs to fire an event. I naively set it up as follows:

*// composable.js*
import { defineEmits } from "vue";

export default function useComposable() {
  // Vars
  let buffer = [];
  let lastKeyTime = Date.now();
  const emit = defineEmits(["updateState"]);

document.addEventListener("keydown", (e) => {
    // code
    emit("updateState", data);
   }

// *App.vue*
<template>
<uses-composables
    v-show="wirtleState.newGame"
    @updateState="initVars"
  ></uses-composables>
</template>
<script setup>
const initVars = (data) => {
//code here

}

// usesComposable.vue
<template>
  <button @click="resetBoard" class="reset-button">Play Again</button>
</template>

<script setup>
import { defineEmits } from "vue";
import useEasterEgg from "@/components/modules/wirdle_helpers/useEasterEgg.js";


useEasterEgg();
</script>

The error I get is "Uncaught TypeError: emit is not a function useEasterEgg.js:30:11

So obviously you can not use defineEmits in a .js file. I dont see anywhere in Vue docs where they specifically use this scenario. I dont see any other way to do this but using $emits but that is invoked in a template which my composable does not have. Any enlightenment much appreciated.

3

There are 3 best solutions below

1
Lx4 On

You can't access emit this way, as the doc says : defineProps and defineEmits are compiler macros only usable inside script setup. https://vuejs.org/api/sfc-script-setup.html

I'm not entirely sure of what you are trying to achieve but you can use vue-use composable library to listen to key strokes https://vueuse.org/core/onkeystroke/

Lx4

2
Hugo On

You can emit events from a composable, but it will need to know where the events should be fired from using context which can be accessed from the second prop passed to the setup function: https://vuejs.org/api/composition-api-setup.html#setup-context

Composable:

    export default function useComposable(context) {
      context.emit("some-event")
    }

Component script:

    <script>
    import useComposable from "./useComposable"
    export default {
       emits: ["some-event"],
       setup(props, context) {
           useComposable(context)   
       }
    }
    </script>
2
Hyphæne Ohmen On

To use it in a script setup, the best way I found was to declare the defineEmit first, and assigning it to a const, and pass it as a param to your composable :

const emit = defineEmit(['example']
useMyComposable(emit);

function useMyComposable(emit){
   emit('example')
}