Skip to content

Commit a315c31

Browse files
committed
fix: remove event listeners on form attachment cleanup
Fixes #14546 The call to `enhance()` returns a new object each time with a new attachment. And because we didn't remove the event listeners on attachment cleanup this created more and more listeners over time
1 parent 3cbaac2 commit a315c31

File tree

5 files changed

+49
-4
lines changed

5 files changed

+49
-4
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/kit': patch
3+
---
4+
5+
fix: remove event listeners on form attachment cleanup

packages/kit/src/runtime/client/remote-functions/form.svelte.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,8 @@ export function form(id) {
319319

320320
form.addEventListener('submit', onsubmit);
321321

322-
form.addEventListener('input', (e) => {
322+
/** @param {Event} e */
323+
const handle_input = (e) => {
323324
// strictly speaking it can be an HTMLTextAreaElement or HTMLSelectElement
324325
// but that makes the types unnecessarily awkward
325326
const element = /** @type {HTMLInputElement} */ (e.target);
@@ -398,17 +399,24 @@ export function form(id) {
398399
name = name.replace(/^[nb]:/, '');
399400

400401
touched[name] = true;
401-
});
402+
};
403+
404+
form.addEventListener('input', handle_input);
402405

403-
form.addEventListener('reset', async () => {
406+
const handle_reset = async () => {
404407
// need to wait a moment, because the `reset` event occurs before
405408
// the inputs are actually updated (so that it can be cancelled)
406409
await tick();
407410

408411
input = convert_formdata(new FormData(form));
409-
});
412+
};
413+
414+
form.addEventListener('reset', handle_reset);
410415

411416
return () => {
417+
form.removeEventListener('submit', onsubmit);
418+
form.removeEventListener('input', handle_input);
419+
form.removeEventListener('reset', handle_reset);
412420
element = null;
413421
preflight_schema = undefined;
414422
};
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<script>
2+
import { get_count, increment } from './form.remote.js';
3+
const count = get_count();
4+
</script>
5+
6+
<p id="count">{count.current}</p>
7+
8+
<!-- this looks weird but forces the spread into being reactive -->
9+
<form {...increment.for((count.current || 1) && 'a').enhance(async ({ submit }) => await submit())}>
10+
<button type="submit" id="submit">Submit</button>
11+
</form>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { form, query } from '$app/server';
2+
import * as v from 'valibot';
3+
4+
let count = 0;
5+
6+
export const get_count = query(() => count);
7+
8+
export const increment = form(v.object({}), async () => {
9+
count++;
10+
await get_count().refresh();
11+
});

packages/kit/test/apps/async/test/client.test.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,4 +315,14 @@ test.describe('remote function mutations', () => {
315315

316316
await expect(page.locator('h1')).toHaveText('hello from remote function!');
317317
});
318+
319+
test('form.for() with enhance does not duplicate requests', async ({ page }) => {
320+
await page.goto('/remote/form/for-duplicate');
321+
322+
for (let i = 1; i <= 3; i++) {
323+
await page.click('#submit');
324+
await page.waitForTimeout(100);
325+
await expect(page.locator('#count')).toHaveText(String(i));
326+
}
327+
});
318328
});

0 commit comments

Comments
 (0)