Skip to content

Invalidation during parent out:, can cause child in: to play, or out: to stallΒ #9198

@robertadamsonsmith

Description

@robertadamsonsmith

Describe the bug

There are two related bugs. During an outro in a parent component, if the child component has a variable invalidated which an #if block is dependant on, and the #if block contains an element with its own transition, then either:

  • if the transition is |local, the intro transition will unexpectedly start playing
  • if the transition is |global, the parents outro will very unexpectedly stall at the end, leaving all elements visible and unmounted

(REPLs below)

There are several issues that are ultimately due to this, and I've encountered both bugs myself, so I think it could be quite widespread: #8606 #8351 #5268 #9186

It appears that the first bug occurs because when the transition is |local the generated fragment doesn't track current so that when the up()date function calls transition_in, it is unable to early exit the i()ntro. It does actually do that correctly when |global is used though, so it might just be a case of using that logic more generally.

I'm not yet sure why the second bug is occurring, but I assume the logic is highly related.

Reproduction

This is the minimal test case that demonstrates the first behaviour:
https://svelte.dev/repl/b558e1303d1f45848b4dffc765c806dc?version=4.2.0

App.svelte

<script>
	import Child from "./Child.svelte"
	import {fade} from "svelte/transition"
	import {writable} from "svelte/store"

	let state = writable({});
	let show = true;

	function toggle() {
		$state = {};
		show = !show;
	}
</script>

<button on:click={toggle}>Toggle</button>

{#if show}
	<div transition:fade>
		Parent
	</div>	
	<Child {state} />
{/if}

Child.svelte

<script>
	import {fade} from "svelte/transition"
	
	export let state;
</script>

{#if true || $state}
	<div transition:fade>
		Child
	</div>
{/if}

The requirements are as follows:

  • There must be a parent and child component (if the child is inlined into the parent, the bug does not occur)
  • The parent must have a conditional block which becomes falsy, containing both a transition: element, and the child component
  • While the parent component's outro is playing (or at the same time as it starts), a variable in the child component must become invalidated. This is typically done by having a shared store be updated with a non-primitive value (but could also be from some other mechanism, such as calling a function exported from the child component that invalidates a value within the component to a non-primitive value)
  • The child component must have a conditional block which is apparently dependent on the invalidated variable, and which contains a transition: element (note that the conditional block can even be 'fixed' to always be true, so the value of the variable doesn't matter, only that it has been invalidated)
  • The child component's element will now start playing its intro transition

The second bug can be demonstrated by just changing the Child transition to |global:
https://svelte.dev/repl/74b66e56366c491baf37f4baa4a1c347?version=4.2.0

This has the following effect:

  • When the outro finishes, all elements that should have been removed remain mounted and visible
  • The child component's element won't play its intro transition though (silver linings)

Logs

No response

System Info

4.2.0

Severity

annoyance

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions