onMount within action will not fire for initially hidden component
Describe the bug
If onMount is invoked from within a use:action function, and is declared on a child node within an initially unmounted component, the code within onMount will not run when said component is mounted.
However, if the component is set to mount when App.svelte is mounted, the code within use:action's onMount will run, though the returned function intended to be called through onDestroy will not run when the component is destroyed.
This issue originates from this discord thread
Reproduction
https://svelte.dev/repl/7aa63b40621344c1a7459516c2ca03a4?version=3.49.0
Logs
No response
System Info
N/A
Severity
annoyance
Since Action receives an HTMLElement, so it cannot follow below rule.
https://svelte.dev/docs#run-time-svelte-onmount
It must be called during the component's initialisation
I have implemented an alternative. Would this be a workaround?
https://svelte.dev/repl/aa220a4dc8ef4ab9ad79faa7feb7200d?version=3.49.0
@baseballyama I'm the original poster of the discord discussion. Thanks for your input. It is certainly a workaround. But for a self-contained action (say an action that does some internal stuff when component has been mounted), requiring users to manually execute the exported onMountProcess is probably not the best experience.
And now that i have thought about it, you are right. action is executed when HTMLElement is created, not during component initialization. In fact, if we look at the compiled code in the original repl, the action is called during the mount instruction:
// portion of compiled JS of `C1.svelte`
m(target, anchor) {
insert(target, main, anchor);
if (!mounted) {
dispose = action_destroyer(action_action = action.call(null, main));
mounted = true;
}
},
So the bottom line is we should not use onMount in action, although the fact that it sometimes works still bothers me (try removing the if condition in the original repl).
For people coming to this thread, here is my workaround (thanks Geoff Rich)
import { tick } from 'svelte';
export function yourAction() {
// ...
tick().then(() => {
// do stuff for when mounted
});
// ...
}
FWIW, I have also tried setTimeout(..., 0) in place of tick and it works as well, although i think we should stick to tick here. Also, I imagine that beforeUpdate and afterUpdate can be used in action and might come in handy in some use cases.
@tanhauhau (sorry for tagging) consider you've made this wonderful video about component initialization, perhaps you can give some insights and decide whether this is a proper issue (or close it)?
How about something like that? https://svelte.dev/repl/237823ed53614c60902db0745dff0d6e?version=3.49.0