-
-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Description
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.
| increment(this.#version); |
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
Logs
System Info
REPLSeverity
annoyance