Skip to content

SvelteURLSearchParams: Reading size causes unnecessary renders on related SvelteURL.href changesΒ #17218

@webJose

Description

@webJose

Describe the bug

Effects are run every time the URL doesn't change at all. I know, it sounds strange. In more detail: Effects that have read the SvelteURLSearchParams.size property are re-run every time the SvelteURL.href property is set, even when this doesn't affect URL search parameters at all.

Potential Consequences

I know, the above doesn't seem like much, but it really kind of is. In my case, it was causing infinite effect loops. I'll describe a bit further my case.

My Scenario

I made a SPA/PWA router for Svelte using all-native Svelte v5 API. For the curious: @svelte-router/core. I am adding the ability to redirect URL's using a class that sets up an effect.

The effect is supposed to run whenever the location URL changes, or whenever the list of redirections changes. The former is the important one for this scenario.

Whenever location changes, the list of redirections is tested against the URL, and if a match is found, the router navigates to the specified new URL.

Because of the library's very unique nature, there's a non-trivial "final URL" calculation performed before actually navigating. One of the perks this algorithm offers is the ability to preserve existing query string values. This is where SvelteURLSearchParams comes into play: The library uses a SvelteURL (that comes with one instance of SvelteURLSearchParams) to create the reactive version of the window's location URL.

This "preserve query" feature will read the query parameters' size value to see if there's anything to work with. This is the "unwanted" signal read that happens when the redirector object navigates (redirects). Because of this signal, the effect is re-run, and if the URL still has the conditions to warrant redirection (navigation), then it happens again, and again, and again until the end of time! No, just until Svelte says "enough!". πŸ˜„

Cause

The problem is the class' internal replace method, which blindly increments the artificial #version signal.

Proposed Solution

Since it would be messy and risky to introduce a separate signal just for size, I propose converting SvelteURLSearchParams.size to a derived. Derived's seem to be capable of stopping the propagation of artificial signals, at least for scalar values.

Derived as a Guard for Artificial Signals

In this REPL, one can see the guard in action in two places: The console log, where we only observe $inspect's initial entry for doubleSize, and then the log that doesn't grow whenever we operate on the test class but the operation doesn't affect the value of the size property. Note how inspecting directly for Test.size does trigger updates in the console. The derived value, however, stays cool.

I think this demonstrates that we can still continue to rely using the artificial signal, but by making use of $derived we can stop unnecessary re-renders, and in my case, infinite effect loops.

If you guys are tied up, I can make the PR (I think!!). Do let me know.

Reproduction

Svelte REPL

Logs

System Info

REPL

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