Skip to content

fix(forms): align Vue, Svelte and Solid submit handling with React - #761

Merged
cpojer merged 2 commits into
voidzero-dev:mainfrom
yangshun:fix/form-overlapping-submits
Nov 9, 2026
Merged

cpojer merged 2 commits into
voidzero-dev:mainfrom
yangshun:fix/form-overlapping-submits

Conversation

@yangshun

@yangshun yangshun commented Nov 9, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Brings useForm and useIslandForm in Vue, Svelte and Solid in line with React, fixing two bugs in how they handle a submit that is still in flight.

Why the adapters differ

All four adapters used to share these bugs. #738 fixed them in React only, so React has guarded concurrent submits and kept submitted values since then, while Vue, Svelte and Solid have not.

The issues

1. An older submit could still change the form when a newer one was running (3c9ddc5ae)

The forms tracked submits with flags and did not record which one was latest.

  • Page forms: a second submit aborts the first through the router, and the first's finally then set pending = false while the second was still running. A submit button disabled by pending became clickable again mid-request. An older submit that still completed could also apply its errors, success state and defaults.
  • Island forms: nothing aborts the older request, so its response could overwrite a newer result, clear pending, reload the page or redirect, whichever finished first.

2. Edits made during a submit counted as saved (e4ca5a713)

On success, the forms saved their values as of the response rather than as of the submit.

  • hasChanges read false, and reset() restored text that was never sent.
  • resetOnSuccess wiped anything typed while the request was in flight.
  • Page forms sent a shallow copy, so nested values were shared with the live form.

The fix

  • Each form counts its submits with requestId and latestRequestId, the same names React's island form uses. Only the latest submit updates state, reloads or redirects. An older submit's real failure still rejects its own promise.
  • Each form takes a deep copy when submit starts, sends it and saves it as the new defaults.
  • resetOnSuccess only clears the data if it has not changed since submit.
  • The hasChanges and resetOnSuccess docs now cover edits made during a submit. Most of that diff is the formatter repadding the table.

Behaviour that still differs from React

  • Two page-form submits: React's useActionState queues them, while Vue, Svelte and Solid abort the first and keep the latest. This PR keeps that difference. The server may still have processed an aborted request.
  • resetOnSuccess edit check: these adapters compare values, because their form data is mutated in place, while React compares object identity. Typing something and changing it back counts as unchanged here.

Test plan

New tests cover both finishing orders, aborts, older failures, in-flight edits and resetOnSuccess, based on the React test added in #738. Solid's are in a new file because they need its client runtime. They assert through reset() rather than hasChanges, because createStore and createMemo do not track each other under those test mocks.

  • All new tests that assert the bugs failed before their fix (18 for overlap, 9 for in-flight edits)
  • Form tests for all four adapters pass (72)
  • Vue, Svelte and Solid typechecks, format and lint pass
  • vp run check passes (33/33)
  • CI passes on e4ca5a713
  • Local browser tests on the rebased code. vp test run had 16,106 tests pass and 41 fail, every failure being browserType.launch: Executable doesn't exist, because this machine lacks the Chromium build Playwright 1.65 expects

🤖 Generated with Claude Code

yangshun and others added 2 commits November 9, 2026 20:40
…olid

A second submit aborts the first through the router, and the first's
cleanup then cleared `pending` while the second was still running. An older
submit that still completed could also apply its errors, success state and
defaults. Island forms have no abort, so an older response could overwrite a
newer result, reload the page or redirect.

Only the latest submit now updates form state or changes the page, matching
the request ID guard React's useIslandForm gained in voidzero-dev#738. An older submit's
real failure still rejects its own promise.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d Solid

On success these forms saved their values as of the response, so anything
typed while the request was in flight counted as saved: `hasChanges` read
false and `reset()` restored text that was never submitted. `resetOnSuccess`
also wiped those edits, and page forms sent a shallow copy that shared nested
values with the live form.

Each form now takes a deep copy when submit starts, sends it and saves it as
the new defaults. `resetOnSuccess` only clears the data if it has not changed
since submit. This matches what React's forms have done since voidzero-dev#738.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@yangshun
yangshun marked this pull request as draft November 9, 2026 12:40
@yangshun
yangshun force-pushed the fix/form-overlapping-submits branch from 5a81d35 to e4ca5a7 Compare November 9, 2026 12:40
@yangshun
yangshun marked this pull request as ready for review November 9, 2026 12:53
@@ -0,0 +1,259 @@
import { createComponent, createRoot } from 'solid-js';

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid gets a new test file because these tests need its client runtime, since createMemo only evaluates once on the server runtime that use-form.test.ts uses. vi.mock is file-wide, so adding them there would have switched the existing tests too.

@cpojer
cpojer self-requested a review November 9, 2026 22:35

@cpojer cpojer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Starting to feel like all the UI frameworks are the same.

@cpojer
cpojer merged commit bb5865e into voidzero-dev:main Nov 9, 2026
18 checks passed
@yangshun
yangshun deleted the fix/form-overlapping-submits branch November 10, 2026 01:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants