Skip to content

Make exporting empty functions a way to declare component event dispatchers #5602

Closed as not planned
@pushkine

Description

@pushkine

This is a proposal to introduce a more straightforward way to declare component events, namely through the use of empty functions.
Exporting a named function with an empty body would declare an event, and calling that function would dispatch it.

<script lang="ts">
    export function message(msg: string) {}
    message("I just dispatched a message event!");
</script>

The consumer remains the same

<Component on:message={console.log} /> // "I just dispatched a message event!"

This solution comes as a hybrid between prop functions and createEventDispatcher :

export function message(){} export let onMessage = noop createEventDispatcher
on: syntax ❌it's a prop
Event forwarding ❌it's a prop
Amount of listeners ✅ Infinity ❌1 ✅Infinity
Setup ✅ one line ✅ one line ❌>3 lines
"Is there events ?" ✅ it's in the exports ✅ it's in the exports ❌could be anywhere
Arguments ✅ like any function ✅ like any function ❌1 argument
❌wrapped in a CustomEvent
Listener callback ✅ like any function ✅ like any function ❌must destructure CustomEvent
❌must fill type manually
Typings ✅ like any function ✅ like any function ❌troublesome
Refactoring ✅ like any function ✅ like any function ❌troublesome

As a side effect this proposal also introduces the ability to rename events when forwarding them:

<script lang="ts">
    export function hoverLeft(event: MouseEvent) {}
    export function hoverRight(event: MouseEvent) {}
</script>
<button on:mouseover={hoverLeft} /> 
<button on:mouseover={hoverRight} />

The implementation is simple, the compiler just has to fill the body of those functions with createEventDispatcher's :

function message(...args) {
    const listeners = $$self.$$.callbacks.message;
    if (listeners) listeners.slice().forEach((fn) => fn.apply($$self, args))
}

I cannot think of any realistic scenario where this would be a breaking change.

createEventDispatcher has been criticized multiple times #2323 #3488 #4584 #5211 #5597. I for one actively avoid using it. The go-to solution appears to be to introduce yet another reserved variable #5598, I see all of those $$variables slowly creeping up on svelte and I'm very much not looking forward to see another.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions