svelte icon indicating copy to clipboard operation
svelte copied to clipboard

Make exporting empty functions a way to declare component event dispatchers

Open pushkine opened this issue 5 years ago • 3 comments

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.

pushkine avatar Oct 28 '20 04:10 pushkine

this sounds like a big change, i would suggest open a RFC https://github.com/sveltejs/rfcs/ and have a better discussion over there

tanhauhau avatar Jun 29 '21 04:06 tanhauhau

I like the idea of dispatching an event without having to deal with it being an actual Event object, but some downsides:

  • It's not obvious what it does
  • What if you actually just want to export an empty function as a no-op for whatever reason?
  • You may get warnings for unused arguments
  • With on: you'll have an event without an Event object. You might expect to be able to pass the event to a function that handles all events.
  • It might be confusing to have an event handler take different arguments depending on if it's called from on: or addEventListener.
  • Would not work if you want an argument with the same name as an event

probablykasper avatar Dec 01 '21 01:12 probablykasper

Hi it might be naive of me thinking this but for me a fix for what @probablykasper said would look like this:

<script lang="ts">
    export message = createEventDispatcher<string>();//message is of type function ()=> string
    message("I just dispatched a message event!");
</script>

This would make it possible to create and export empty functions without messing with the type system. Would there be any problem with this aproach.

I am a little scared that such a feature will not make it into svelte.

nbrugger-tgm avatar Jun 28 '22 17:06 nbrugger-tgm

An awesome idea! I too came up with something very similar called Auto-dispatch. But it has added default behaviour which I think is very powerful. Svelte is about being svelte after all and createEventDispatcher() is just downright ugly. I think this proposal should be seriously considered. And since it has some minor breaking changes, it could be a part of Svelte 5 which is supposed to be a full rewrite of the Svelte compiler with new features and breaking changes anyway.

ethanlal04 avatar Jul 31 '23 15:07 ethanlal04