Weekly Vue News #105 - Emit Event From Composable

Weekly Vue News #105

Emit Event From Composable

Hi 👋

I'm currently working on a Nuxt application which I'll use to write eBooks about Vue and Nuxt. It uses Nuxt Content, Tailwind and Prince to generate a PDF of my content written in Markdown. It will be a lot of work, but I'm very excited about it.

Have a lovely week ☀️


To support me:

Vue Tip: Emit Event From Composable

Your application may contain a lot of components that are using the same logic. For example, you may have a lot of components that emit the same event. In this case, it might be tempting to create a composable that will emit this event and is used in all components.

If you are using Vue 3 with the <script setup> you define your emits using the defineEmits compiler macro:

App.vue
1<script setup>
2const emit = defineEmits(['change', 'delete'])
3</script>

defineEmits is a compiler macro that is only usable inside <script setup> and compiled away when <script setup> is processed. Thus, we cannot use it inside a composable.

Let's take a look at three possible solutions to emit an event inside a composable without using defineEmits inside of it.

1. Emit from your component

In general, I would always recommend emitting from your component. This is the most straightforward solution and it is easy to understand what is happening.

So you would have a composable with some logic, in this example the composable returns a reactive counter variable and a function to increment the counter:

composable.ts
1import { ref } from 'vue';
2
3export const useEmitOnRefChange = () => {
4 const counter = ref(0);
5
6 const increment = () => {
7 counter.value++;
8 };
9
10 return {
11 counter,
12 increment,
13 };
14};

In our Vue component, we would watch the counter value and emit the event when the counter changes:

App.vue
1<script setup lang="ts">
2import { watch } from 'vue';
3import { Emits } from '../types';
4import { useEmitOnRefChange } from '../composables/useEmitOnRefChange';
5
6const emit = defineEmits<Emits>();
7
8const { counter, increment } = useEmitOnRefChange();
9watch(counter, (newCounter) => {
10 emit('test', `emit-ref-change-${newCounter}`);
11});
12</script>
13
14<template>
15 <button @click="increment">
16 Trigger emit by watching a composable ref
17 </button>
18</template>
2. Pass emit as a parameter

Another solution would be to pass the emit object as an argument to the composable:

composable.ts
1import { Emits } from '../types';
2
3export const useEmitParameter = (emit: Emits) => {
4 const triggerEmit = () => {
5 emit('test', `emit-parameter-${Math.floor(Math.random() * 101)}`);
6 };
7
8 return {
9 triggerEmit,
10 };
11};
App.vue
1<script setup lang="ts">
2import { watch } from 'vue';
3import { Emits } from '../types';
4import { useEmitParameter } from '../composables/useEmitParameter';
5
6const emit = defineEmits<Emits>();
7
8const { triggerEmit } = useEmitParameter(emit);
9</script>
10
11<template>
12 <button @click="triggerEmitParameter">
13 Trigger emit from composable with emit as parameter
14 </button>
15</template>
Use getCurrentInstance

The last solution is to use the getCurrentInstance function from Vue to get the emit object from the current component instance:

composable.ts
1import { getCurrentInstance } from 'vue';
2import { Emits } from '../types';
3
4export const useEmitFromCurrentInstance = () => {
5 const { emit }: { emit: Emits } = getCurrentInstance();
6
7 const triggerEmit = () => {
8 emit('test', `emit-current-instance-${Math.floor(Math.random() * 101)}`);
9 };
10
11 return {
12 triggerEmit,
13 };
14};

getCurrentInstance is an undocumented internal API that you should use with caution.

It was originally documented in October 2020 but later removed in August 2021.

StackBlitz Demo

All three solutions are implemented in this StackBlitz demo:

Quote of the week
Curated Vue & Nuxt Content
📕 A Comprehensive Vue 2 to Vue 3 Migration Guide
👉🏻 This article takes you through the seamless transition from Vue 2 to Vue 3.
👉🏻 It also covers how to handle the deprecated and eliminated parts during the migration process.
medium.com
📕 Mastering Internationalization: A Guide to Localization in Vue Apps with Vue I18n
👉🏻 This article guides you through concepts of localization using Vue I18n
👉🏻 It also shows how to implement useful features like lazy loading locales
venkata-sandeep-takasi.hashnode.dev
📕 Vue 3 for React developers: Side-by-side comparison with demos
👉🏻 The goal of this article is to offer React developers a quick introduction to Vue development.
dev.to
📕 API Management in Nuxt 3 with TypeScript
👉🏻 Within a large Nuxt application, it's common to work with a bunch of data, consumed from numerous APIs.
👉🏻 The repository design pattern can help to handle your API neatly and efficiently.
www.vuemastery.com
📕 Building a 3D Scene in Nuxt with TresJS
👉🏻 In this guide, you'll explore how to build a 3D scene in a Nuxt 3 app using TresJS using the official Nuxt Module.
www.vuemastery.com
🛠️ Radix Vue
👉🏻 Radix Vue is a Vue port of Radix UI.
👉🏻 Unstyled, accessible components for building high‑quality design systems and web apps in Vue.
www.radix-vue.com
Fun
Curated Web Development Content
📕 How to Choose Your Stack Rationally
👉🏻 This article explores various factors to consider when choosing your stack.
levelup.gitconnected.com
📕 The Regular Expressions Book
👉🏻 If you want to master regular expressions and understand how they work in JavaScript, this book's for you.
www.freecodecamp.org
📕 Type vs Interface: Which Should You Use In 2023?
👉🏻 "The TS team recommend you go with personal preference, but default to 'interface'."
👉🏻 Matt agrees "except that declaration merging should feel scary enough for you to default to 'type'. "
www.totaltypescript.com
📕 Node.js Best Practices in 2023
👉🏻 This repository is a summary and curation of the top-ranked content on Node.js best practices.
👉🏻 It presents more than 80 best practices, style guides and architectural tips.
github.com
🛠️ Kysely
👉🏻 The type-safe SQL query builder for TypeScript
kysely.dev

Comments? Join the discussion about this issue here.

Until next week,

Unsubscribe from this newsletter
Holzapfelkreuther Str. 19, 81375 Munich, Germany

评论

此博客中的热门博文

Scripting News: Tuesday, June 11, 2024

Scripting News: Tuesday, February 13, 2024