Vue-emit: an npm package to dispatch events in Vue.js
Tags: javascript, vue, npm, showdev
Published: 2018-10-21 11:49:35

---
title: Vue-emit: an npm package to dispatch events in Vue.js
published: true
description: A handy npm package that provides an API to emit events in functional components similar to Vue.js $emit
tags: javascript, vue, npm, showdev
cover_image: https://thepracticaldev.s3.amazonaws.com/i/3ykgdukqhp8ncwkcr87e.jpg
---

[Source code](https://github.com/denisinvader/vue-emit) | [NPM](https://www.npmjs.com/package/vue-emit)

Vue.js provides an API for dispatching events in components by `this.$emit`. But in functional components, you don't have `this` instance at all and many people who didn't previously worked with functional components, ask "How to emit an event in a functional component?"

The first solution is to use props for event listeners. It such a React way. The problem that in this case, you have strictly defined event name, can't use `@` / `v-on` / `v-model` syntax and have some additional difficulties in implementing [transparent wrappers](https://www.vuemastery.com/conferences/vueconf-2018/7-secret-patterns-vue-consultants-dont-want-you-to-know-chris-fritz/). Example:
```jsx
// Child component
export default {
  name: 'base-input',
  functional: true,
  props: {
    onInput: {
      type: Function,
      default: () => (),
    },
  },
  render (h, context) {
    return (
      <input
        onInput={context.props.onInput}
        {...{
          attrs: context.data.attrs,
          on: context.listeners,
        }}
      />
    );
  },
};
```
```vue
<!-- Parent component template -->
<template>
  <base-input onInput="doSomething" />
</template>
```

The second solution is to use `context.listeners`. This is a good way to dispatch an event in Vue.js. Example:
```jsx
// Child component
export default {
  name: 'base-input',
  functional: true,
  render (h, context) {
    return (
      <input
        onInput={e => context.listeners.input(e.target.value)}
        {...{
          attrs: context.data.attrs,
          on: context.listeners,
        }}
      />
    );
  },
};
```
```vue
<!-- Parent component template -->
<template>
  <base-input @input="doSomething" />
</template>
```

But there are some pitfalls when using `context.listeners`. I've found two situations when you will get `TypeError: context.listeners.yourEvent is not a function`

### 1. Listener can be `undefined` (not proveded)

If you don't pass a listener from the parent component, there is no handler for the event (input event in our case) in `context.listeners` object.

### 2. Listener value can be a function

![Components structure example](https://thepracticaldev.s3.amazonaws.com/i/0mimq45c5trvz1ik4byt.jpg)

The picture above shows the example structure of components and listeners. It this structure `context.listeners.input` in the C component will be an array of two functions: `[doSomethingA, doSomethingB]`. This is also true for `this.$listeners`.

So when using `context.listeners` (either directly calling handlers from `this.$listeners`) you have to always check that listener exists and what type it has: Function or Array.

### Using vue-emit

To solve above problems I've wrote the helper function that do all checks and provides the API similar to `$emit`. Thats how to use it:

```sh
yarn add vue-emit
```
```jsx
import emit from 'vue-emit';

export default {
  name: 'base-input',
  functional: true,
  render (h, context) {
    return (
      <input
        onInput={e => emit(context.listeners, 'input', e.target.value)}
      />
    );
  },
};
```

The first argument is an object of listeners with `eventName => handler` structure. It is a standard Vue `this.$listeners` and `context.listeners` objects.

The second argument is an event name. Vue-emit will check listeners object and do nothing if handler is not defined or will call each handler if there are more than one listener.

You can also pass any amount of additional parameters and they all will be passed to handler. By the way, this is the use case with stateful components - when for some reason, you want to pass more than one argument to handler function as a payload.

## Conclusion 

I hope you will find this package useful and enjoyed reading this article. Feel free to ask your questions, open issues, pull request etc. 

{% github denisinvader/vue-emit %}


Photo by Daniele Riggi on Unsplash