svelte: Store updates break transitions and components
Describe the bug Components with an out:[transition] and a store subscription don’t get destroyed if the store is updated while out:[transition] is in progress.
To Reproduce https://svelte.dev/repl/00d38994d0364ea3a3ae629937142ea6?version=3.12.1
- Create a store
- Subscribe to it in component A
- Create an element with
out:fadein component A - Destroy component A and trigger a store update before the animation has completed Result: component A never got destroyed.
Expected behavior Components should be destroyed
Severity Very. It breaks apps without a single warning or error. This means that the more complex/nested your app is, the harder it is to troubleshoot.
Additional context Any app that relies on URL params to update the store is highly susceptible to this bug. I ran into it with Sapper as well.
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 15
- Comments: 15 (6 by maintainers)
Fixed in 3.21.0 - https://svelte.dev/repl/00d38994d0364ea3a3ae629937142ea6?version=3.21.0
I can confirm this is still an issue as described, except the threshold I found was 240ms. Anything above that and this bug occurs; 240ms or below, it works as expected.
Here you can see an example REPL: https://svelte.dev/repl/c2ec0888fcf044f78b0ab05c446d5787?version=3.24.1
I am having the same issue in a rather large scale app. When I add out-transitions, parts of the app are not unmounted anymore.
Today I finally took some time (well, more or less the whole day…) to debug this.
Based on the excellent example above I built a modified, further simplified version (here as a REPL - based on the initial one above). No store and only one child.
Findings:
But: I am not sure if it requires to add a global/local classifier. When running is set to false, the Set “outroing” still contains two entries. Could this be used to prevent cancelling the overall outro, or pick up at the remaining entries?
I am most probably missing something, but this worked for me (both the the simple example as well as the one from above. Also tried this out in a large scale app):
Penny for your thoughts…
Edit: As I expected I missed something. After a good night of sleep I had a second look at this and realized that outroing obviously also contains all other outros… Will try to find some more time for this later this week.
I have done some digging and discovered the following during a trace: The outros are all scheduled as usual then an update kicks in and the if block is transitioned back in
This goes through the motions and ends up finding the header which is transitioning out and cancels that outro (since it is coming back in now)
the end sets
running= falsefor the entire outro groupSo I think the root cause is that the outro is being cancelled and it shouldn’t be. I think this is a case of a local intro canceling a global outro.
We can see that the intro call doesn’t have enough information, it can’t tell if the outro was created for a local transition or not.
Do we need to store the type (local|global) as well as the transition?
This explains my current struggles. Does anyone know of a temporary fix for this? (how to forcefully remove after transition). I tried triggering a
{#key}on:outroendbut it doesn’t always work.@Conduitry Could we re-open this issue?
This also affects Sapper, where any transition on the page whose elements are provided by the contents of a store (such as an each) cause navigation to break (and then the app to break entirely).
@jhwheeler there are a few workarounds. They’re all duct tape solutions though.
EDIT: Added no. 4