svelte: Infinite loop when binding to an item in a for loop

Describe the bug

The page freezes when I attempt to iterate through a map containing a class and bind a freshly created element using data from that class into a field of that class. This appears to be related to #6921 Considering that one of my cores spikes up to 100%, there is probably some infinite loop going on. I am a beginner, so I am sorry if I am making some rookie mistake

Reproduction

I have this example:

<script lang="ts">
    import "../app.css";
    import {NavbarRoute} from "../lib/NavbarRoute.ts";
    import {onMount} from "svelte";

    let beforeShop = new Map<string, NavbarRoute>;

    beforeShop.set("/", new NavbarRoute("Home"));
    beforeShop.set("/tos", new NavbarRoute("Pravidla a GDPR"));

    let afterShop = new Map<string, NavbarRoute>;

    afterShop.set("/team", new NavbarRoute("Tým"));
    afterShop.set("/hire", new NavbarRoute("Nábory"))
    afterShop.set("/contact", new NavbarRoute("Kontakt"));

    onMount(function () {
        let route;

        route = beforeShop.get(window.location.pathname);
        if (route == undefined) route = afterShop.get(window.location.pathname);

        if (route) {
            let div = route.div;

            div.classList.add("tab-active");
        }
    })
</script>

<header>
    <div class="bg-base-100 w-screen inline-flex items-center pt-2 pb-2">
        <div class="w-1/2 justify-start"></div>
        <div class="flex-shrink-0 inline-flex">
            <div>
                {#each [...beforeShop] as [path, route]}
                    <nav-el bind:this={route.div}>{route.name}</nav-el>
                {/each}
                <nav-el>Wiki</nav-el>
            </div>
            <div>
                <nav-el class="bg-primary text-base-100 rounded-box">Shop</nav-el>
            </div>
            <div>
                {#each [...afterShop] as [path, route]}
                    <nav-el bind:this={route.div}>{route.name}</nav-el>
                {/each}
            </div>
        </div>
        <div class="w-1/2 justify-end flex">
            <nav-el class="">Přihlásit se</nav-el>
        </div>
    </div>
</header>

NavbarRoute:

export class NavbarRoute {

    public div: any | undefined;

    constructor(public name: string) {
        this.div = undefined;
    }
}

By commenting out some parts of the code, I’ve found out, that it hangs due to: bind:this={route.div}

Logs

Nothing showed up in the server and browser logs.

System Info

System:
    OS: Linux 5.18 Arch Linux
    CPU: (24) x64 AMD Ryzen 9 5900X 12-Core Processor
    Memory: 44.10 GB / 62.71 GB
    Container: Yes
    Shell: 5.1.16 - /bin/bash
  Binaries:
    Node: 16.16.0 - /usr/bin/node
    Yarn: 1.22.19 - /usr/bin/yarn
    npm: 8.15.1 - /usr/bin/npm
  Browsers:
    Chromium: 104.0.5112.79
    Firefox: 103.0.1
  npmPackages:
    svelte: ^3.44.0 => 3.49.0

Severity

blocking all usage of svelte

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 16 (12 by maintainers)

Most upvoted comments

This is fixed in Svelte 5, because dependencies are tracked at runtime and the infinite loop is thus avoided

However, if I understand correctly, #5689 is a duplicate to #4447, which should be fixed.

I’ve been triaging Svelte issues for well over a year now and seen every single issue and this comes up regularly, it is definitely not fixed (at least not every variation of it). I even ran into it myself over year ago https://github.com/sveltejs/svelte/issues/6298

To make this very clear, here is a REPL https://svelte.dev/repl/5f436e05809346a38baf906184321761?version=3.49.0 It logs obj twice, for no reason.

I was able to track this down to the use of the spread operator. Or more specifically cloning the array, slice works too. Here’s the absolute minimal REPL

https://svelte.dev/repl/d8f6f4532ab04a1fa028d1835d55839e?version=3.49.0

The first two each cause infinite loops, the third does not.

Sorry, I thought that it was unrelated to the original issue, do you have any idea how to transform this one-liner to sveltekit?

SvelteKit offers a whole host of information for this precise purpose. E.g. https://kit.svelte.dev/docs/modules#$app-stores-page allows you to access the current URL on both server and client.

If you follow the Getting Started https://kit.svelte.dev/docs/introduction#getting-started you can see that in the demo app:

	<nav>
		<svg viewBox="0 0 2 3" aria-hidden="true">
			<path d="M0,0 L1,2 C1.5,3 1.5,3 2,3 L2,0 Z" />
		</svg>
		<ul>
			<li class:active={$page.url.pathname === '/'}><a sveltekit:prefetch href="/">Home</a></li>
			<li class:active={$page.url.pathname === '/about'}>
				<a sveltekit:prefetch href="/about">About</a>
			</li>
			<li class:active={$page.url.pathname === '/todos'}>
				<a sveltekit:prefetch href="/todos">Todos</a>
			</li>
		</ul>
		<svg viewBox="0 0 2 3" aria-hidden="true">
			<path d="M0,0 L0,3 C0.5,3 0.5,3 1,2 L2,0 Z" />
		</svg>
	</nav>

https://github.com/sveltejs/kit/blob/5cd8385391e30ecf525d11d24c268d3b08284dfb/packages/create-svelte/templates/default/src/lib/header/Header.svelte

Edit: I recommend checking out the demo app before working with an empty project.