How to use custom event handlers in Vue 3 with Composition API and TSX templates?
Asked Answered
P

1

6

I'm struggling to understand how one is supposed to work with custom events in Vue 3 with TSX templates.

Consider a simple parent/child app where child sends a custom event to parent:

const Child = defineComponent({
    emits: {
        boom: (x: number) => true,
    },
    setup(props, { emit }) {
        console.log("Child props", props)
        return () => (
            <div onClick={() => emit("boom", 123)}>Click me!</div>
        )
    },
})

const Parent = defineComponent(() => {
    return () => <Child onBoom={(x) => console.log(`Boom: ${x}`)} />
})

The above functions properly, and:

  • In child setup, props is empty (as I would expect).
  • emit call in child is properly typed (it requires a number to be passed as argument).

Unfortunately, it doesn't know how to type onBoom in parent:

  Property 'onBoom' does not exist on type 'IntrinsicAttributes & Partial<{}> & Pick<Readonly<{} & {}> & VNodeProps & AllowedComponentProps & ComponentCustomProps, "style" | ... 8 more ... | "class">'.ts(2322)

Now if I add onBoom property definition to Child:

const Child = defineComponent({
    props: {
        onBoom: Function as PropType<(x: string) => void>,
    },
    emits: {
        boom: (x: number) => true,
    },
    ...

Then it still functions properly, and onBoom in parent becomes typed, but:

  • Developer must repeat event definitions in two places.
  • Nothing prevents these two definitions from going out of sync. In the example above, did you notice that I typed x: string instead of x: number in props.onBoom by mistake?
  • In child setup, props is now not empty, it is an object with onBoom property.

My question is: what is the "correct" way to handle custom events with Vue 3, Composition API and TSX templates?

Honestly, I am tempted to disregard the whole emits/emit machinery and simply use React-style callbacks with:

<div onClick={() => props.onBoom?.("five")}>

(What stops me at the moment is that I know that it may break when I decide to go with SSR, as these properties won't hydrate.)

Pendergast answered 19/1, 2021 at 18:17 Comment(1)
Hi. Did you ever find a solution for this?Krebs
E
0

In your template the emit option is available globally so you can do this

<button @click="$emit('onBoom')">onBoom</button>
Eustatius answered 4/6, 2022 at 6:20 Comment(1)
Thank you for the advice. However, that doesn't solve the problem outlined in the question (typing error in Parent component).Pendergast

© 2022 - 2024 — McMap. All rights reserved.