withastro / astro

The web framework for content-driven websites. ⭐️ Star to support our work!
https://astro.build
Other
46.81k stars 2.49k forks source link

Astro + Svelte: ViewTransitions do not persist state and do not remove eventlisteners (from version 4.5.0 onward, until 4.4.15 it works) #11970

Closed CaspianVahagn closed 1 month ago

CaspianVahagn commented 2 months ago

Astro Info

Astro                    v4.15.4
Node                     v20.17.0
System                   Linux (x64)
Package Manager          unknown
Output                   static
Adapter                  none
Integrations             @astrojs/tailwind
                         @astrojs/svelte

If this issue only occurs in one browser, which browser is a problem?

No response

Describe the Bug

When using thetransition:persist directive on svelte-components two errors occur:

  1. The state is not persisting (which is ok since you can opt out with transition:persist-props)
  2. The eventlisteners are not removed (e.g. on:click)

The second issue is extremly problematic, since it will stack all eventlisteners on rerouting:

In the example provided is a simple counter component made with svelte, with the viewtransition persist directive.

<header style="padding:3rem ">
    <a style="color:white" href="/hello">hello</a>
    <a style="color:white" href="/">home</a>
    <Counter client:load transition:persist></Counter>
</header>

Counter.svelte:


<script lang="ts">
    import {sampleState} from "../state/sampleState.ts";
    import {onMount} from "svelte";

    function update(){
        sampleState.update(e => e+1)
        console.log("update")
    }
    onMount(() => {
        console.log("mounted")
    })
</script>
<button style="padding: 1rem;" on:click={update}>{$sampleState}</button>

The update function will be called with each click, multiplied with amount of navigations when the user uses a link to navigate. The example has two links and pages: "home", "hello". As soon as you navigate by link, the component gets rerendered and the eventlisteners of on:click will not get destroyed. The component will be mounted multiple times (per navigation) but the eventlistener will not get destroyed, leading to the "update" function getting called multiple times per click. Hence it will not count by +1, but count by 1 + navigations.

On destroy and On Mount will be called, per navigation.

This bug is appearing from version 4.5.0 Onward. I discovered it when upgrading from 4.3.7.

The latest version in which transition:persist works correctly is: 4.4.15 . I hope this may help you

Current solution is: You have to opt out of the rerender with "transition:persist-props"

What's the expected result?

Events are only emitted once. State is persisted. And if component gets destroyed and rerendered, all framwork defined eventlisteners get removed.

With Astro Version 4.4.15 this problem did not occur. Here is the example with that version: https://stackblitz.com/edit/github-xyb8rc-rruld2

Counter counts. Component trigger only one callback per click. State is persisted

Link to Minimal Reproducible Example

https://stackblitz.com/edit/github-xyb8rc

Participation

martrapp commented 2 months ago

Hi @CaspianVahagn, I'm sorry for the trouble you got with the version update. As you said, the behavior of transition:persist was changed in 4.5 and adding transition-persist:props is the way to switch back to the previous behavior, see the CHANGELOG entry for 4.5

There is also a PR to activate the former behavior if the properties did not change (https://github.com/withastro/astro/pull/11915).

martrapp commented 1 month ago

Hi @CaspianVahagn,

now that astro@4.15.7 and @astrojs/svelte@v5.7.1 are out, could you please confirm that npx @astrojs/upgrade solves your issue?

martrapp commented 1 month ago

Closed due to inactivity. If there are still issues despite the fixes mentioned above, please open again.