I created a reproduction for an issue with Svelte dispatch not being called:
Open the reproduction
Open the browser console
Go on the "About" page using the link
I sould see the message: "handleInput" but I'm not
<script>
import Select from 'svelte-select';
import { createEventDispatcher } from 'svelte';
export let value = undefined;
export let id = undefined;
const dispatch = createEventDispatcher();
let result;
let items = [
{ value: 'one', label: 'One' },
{ value: 'two', label: 'Two' },
{ value: 'three', label: 'Three' },
];
$: if (id !== undefined) {
result = id;
}
$: if (result != undefined) {
value = { value: 'custom', label: 'Custom' };
console.log("this should dispatch!")
dispatch('input', value);
console.log("is it dispatched?")
}
</script>
<Select {value} {items} on:change on:input />
The dispatch('input', value)
is completely ignored.
Why?
Reproduction steps are:
Your reactive statements are firing before the on:input
event listener attaches to the <Select>
component. This may be due to the fact that reactive statements are called before the DOM (or component markup) mounts, and event listeners are attached after the component mounts.
The reason your setTimeout
code works is because it adds a microtask that likely registers after the event listeners get set up; so when its callback function fires, your on:input
listener will receive the dispatched event. Similarly, using await tick()
makes your code work for the same reason:
async function dispatchEvent () {
value = { value: 'custom', label: 'Custom' };
console.log("this should dispatch!")
// wait for all microtasks to complete before continuing
await tick()
dispatch('input', value);
console.log("is it dispatched?")
}
$: if (result != undefined) {
dispatchEvent()
}
With that said, having many reactive statements is challenging to follow. I recommend refactoring to use event listeners in your CustomSelect.svelte
, which can be combined with event forwarding:
<Select on:input={internalInputHandler} on:input on:change />
<!-- ^ handle input ^ forward input -->
I also recommend dispatching events with a custom name, rather than override native event names; that way you can handle them separately and expect the native InputEvent
for on:input
. You could name your dispatched event input--id
:
<!-- Component.svelte -->
<script>
function handler() {
dispatch('input:component', { some: 'detail' })
}
</script>
<!-- +page.svelte -->
<Select on:input--id={doSomething} on:input={doSomethingElse} />