From 7e83327400eeaa7e054935439792b696c282c1cf Mon Sep 17 00:00:00 2001 From: cui fliter Date: Tue, 6 Oct 2026 13:22:22 +0800 Subject: [PATCH] fix: avoid tracking SvelteURLSearchParams during URL updates (#18791) ### Before submitting the PR, please make sure you do the following - [x] It's really useful if your PR references an issue where it is discussed ahead of time. In many cases, features are absent for a reason. For large changes, please create an RFC: https://github.com/sveltejs/rfcs - [x] Prefix your PR title with `feat:`, `fix:`, `chore:`, or `docs:`. - [ ] This message body should clearly illustrate what problems it solves. - [ ] Ideally, include a test that fails without this PR but passes with it. - [ ] If this PR changes code within `packages/svelte/src`, add a changeset (`npx changeset`). ### Tests and linting - [x] Run the tests with `pnpm test` and lint the project with `pnpm lint` --- Avoids creating an unintended reactive dependency when `SvelteURLSearchParams` synchronizes its owning `SvelteURL`. `#update_url()` now uses `super.toString()` instead of the reactive `toString()` method. URL synchronization only needs native serialization and should not subscribe the current reaction to the params version signal. Before this change, code such as: ```js render_effect(() => { url.searchParams.set('foo', 'bar'); }); ``` could cause the effect to run twice: 1. set() calls #update_url() 2. #update_url() calls reactive toString() 3. toString() subscribes the effect to #version 4. set() increments #version 5. The effect is scheduled again Signed-off-by: cuishuang --- .changeset/fuzzy-urls-rest.md | 5 +++++ .../svelte/src/reactivity/url-search-params.js | 2 +- packages/svelte/src/reactivity/url.test.ts | 18 ++++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 .changeset/fuzzy-urls-rest.md diff --git a/.changeset/fuzzy-urls-rest.md b/.changeset/fuzzy-urls-rest.md new file mode 100644 index 0000000000..c49215278e --- /dev/null +++ b/.changeset/fuzzy-urls-rest.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: avoid tracking SvelteURLSearchParams during URL updates diff --git a/packages/svelte/src/reactivity/url-search-params.js b/packages/svelte/src/reactivity/url-search-params.js index d745bb6b08..0b87820bfc 100644 --- a/packages/svelte/src/reactivity/url-search-params.js +++ b/packages/svelte/src/reactivity/url-search-params.js @@ -42,7 +42,7 @@ export class SvelteURLSearchParams extends URLSearchParams { if (!this.#url || this.#updating) return; this.#updating = true; - const search = this.toString(); + const search = super.toString(); this.#url.search = search && `?${search}`; this.#updating = false; diff --git a/packages/svelte/src/reactivity/url.test.ts b/packages/svelte/src/reactivity/url.test.ts index 72518d730a..4f597526be 100644 --- a/packages/svelte/src/reactivity/url.test.ts +++ b/packages/svelte/src/reactivity/url.test.ts @@ -115,6 +115,24 @@ test('url.searchParams', () => { cleanup(); }); +test('url.searchParams writes do not create a reactive dependency', () => { + const url = new SvelteURL('https://svelte.dev'); + let runs = 0; + + const cleanup = effect_root(() => { + render_effect(() => { + runs += 1; + url.searchParams.set('foo', 'bar'); + }); + }); + + flushSync(); + + assert.equal(runs, 1); + + cleanup(); +}); + test('url.searchParams.set updates url when duplicate values collapse to the same joined string', () => { const url = new SvelteURL('https://svelte.dev?a=ab&a=c'); const log: any = [];