How to fire an $emit event from Vue Composable

Viewed 1178

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 Answers

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>

This question is a bit confusing. What is <uses-composable>? A composable generally is plan js/ts, with no template. If it had a template, it would just be a component. Even if it was a component, which I mean you could turn it into if thats what you wanted, I don't see anything there that suggests that would be a good idea.

So I guess the question is, why would you want to emit an event? If I'm following what you want, you can just do:

// inside useEasterEgg.js
document.addEventListener('keydown', () => {
    // other code
    const data = 'test';
    updateStateCallback(data);
});

function useEasterEgg() {
  function onUpdateState(callback) {
    updateStateCallback = callback;
  }
  
  return {
    onUpdateState,
  }
}


// Put this wherever you want to listen to the event
const { onUpdateState } = useEasterEgg();

onUpdateState(data => console.log(data));

https://jsfiddle.net/tdfu3em1/3/

Alternatively, if you just want access to data, why not make it a ref and just use it where you want:

const data = ref();

document.addEventListener('keydown', () => {
    // other code
    data.value = resultOfOtherCode;
});

function useEasterEgg() {
  return {
    data,
  }
}


// Anywhere you want to use it
const { data } = useEasterEgg();
Related