svelte: Event is not dispatched after component is loaded

Describe the bug Event from component is not dispatched (from reactive function) after component is loaded.

To Reproduce

https://svelte.dev/repl/2b0b7837e3ba44b5aba8d7e774094bb4?version=3.19.1

Expected behavior

Event is dispatched after component is loaded.

Information about your Svelte project:

  • Your browser and the version: Google Chrome | 80.0.3987.87 (Official Build) (64-bit)
  • Your operating system: Ubuntu 18.04.4 LTS
  • Svelte version: 3.19.1

Severity

Low, I think.

Additional context

When I call dispatch in the setTimeout() with zero delay then it’s working corectlly.

If this is desired behaviour then I think it’s appropriate to write notice to documentation:

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 2
  • Comments: 15 (1 by maintainers)

Most upvoted comments

Would it be possible to display console warning if dispatch was called before onMount or before parent attached listener to event?

Nope. For me it works even without runes mode. Only svelte 5. Thanks for trying! I guess’ll simply wait for the Svelte 5 release and workaround meanwhile. 😃

Nope. For me it works even without runes mode. Only svelte 5.

@Conduitry wrote:

What I think is happening is that instantiating the component is immediately running the $: reactive code, which dispatches the event synchronously, before the parent component attaches the listener.

I suspect that is correct. (Confirmed. See logs below.)

Should we update the title (since “event is not dispatched” is not quite accurate)?: Events dispatched from child component get lost because dispatched before event listeners attached in parent component

I would love to see a diagram in the docs that illustrates this sequence of events in detail (issue forthcoming!)…

I’m currently leaning towards this being something to document rather than being something to change.

I’m still not quite convinced. Wouldn’t Svelte be better if users didn’t have to worry about events getting lost (due to a lifecycle event / component tree initialization step order that they have absolutely no control over), and didn’t have to resort to kludges like await tick() (from #5405) in order to dispatch an event from a child (often, an initial value) in a way that the parent actually receives the event?

Or at least provide an elegant/easy option to synchronize/postpone the timing of these things for cases where that is preferred. Maybe something like:

<Child on:fill|deferUntilSelfMounts={onFill} />

, which would instruct Svelte to queue up any fill events dispatched by <Child> until self (parent) has finished mounting (and therefore finished attaching all listeners)?

Or something like:

<Child on:fill={onFill} svelte:deferEventsUntilSelfMounts />

or

<Child on:fill={onFill} on:*|queueUntilMounted />

to queue and defer all events dispatched by <Child>. Pretty ugly, but just brainstorming…

Seems like that should just be the default behavior…

Note: I believe there may be related confusing race condition (not technically a race condition since it’s deterministic, but what do you call it?) issues related to the use of use: and bind: in a parent component (issues for these may be forthcoming…)…


@MrSrsen wrote:

I was thinking more about this issue and after reading and searching trough some use-cases in our app I think that current behaviours in more appropriate then behaviour that I was originally expecting.

Now I think it’s valid behaviour that reactive statements are not running until onMount is fired.

Technically, that’s incorrect (see @Conduitry’s explanation for the actual reason for the behavior): reactive statements are run before onMount is fired, as this console output (from my fork of your REPL) shows:

Inner: checking isFilled(Some text): true  [called from $: reactivate statement]

Attached listener: ƒ onFill(e) {
    		$$invalidate(0, isFilled = e.detail);
    	}

Inner: onMount: NodeList [input]

This also seems to confirm @Conduitry’s hypothesis that listener isn’t attached in parent until after $: isFilled is called in the child.

Thanks to that I have chance and time to properly initialise all the properties without reactive calls and I do not have to ignore these “initialising” events before proper initialisation.

So it sounds like you would be someone who would actually prefer that the current behavior not change?

I’d be curious to see a concrete example where the current behavior would actually be useful/desirable (and if so, whether the perceived problem might actually be a non-issue in those cases, or easily worked around).

Your original REPL is actually a counterexample/the opposite of what you described here — a case where everything has been sufficiently initialized (the correct values for value and length (“Some text” and 5, respectively) have already been assigned via props by this point) and where it would be more useful to have it always dispatch the fill event, even from the initial invocation of the child’s $: statement.

My case would be resolved by adding event call manually to the onMount function.

You mean (in your initial example) calling isFilled from both $: isFilled(value) and from an onMount callback?

Sure, that works, but seems unfortunate that one needs to have that duplication.

Another option (which I like even better) is to use the tick() workaround from #5405: see my updated REPL. Then you don’t have to add an onMount callback.


@plibither8

Hi, I’ve been struggling with issue for a while now and was wondering whether I’m doing anything wrong on my part. Is there a work-around for this, since the behaviour might not be changed.

I’m still curious if there’s a better workaround, but my personal favorite so far is the await tick() trick from #5405

I’ve reproduced, in a very simple way, what I would like it to do: https://svelte.dev/repl/2b0b7837e3ba44b5aba8d7e774094bb4?version=3.19.1

Oops, looks like you linked to the same REPL as the OP. Did you start from their REPL, make some changes, but forget to save your changes (which would have created a new REPL URL)? Would be interested to see what you were trying to show, about what you would like it to do.