Description
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.