fix(ui): prevent concurrent form submissions on double-click [#2982]#2983
Merged
viniciusdacal merged 1 commit intomainfrom Apr 23, 2026
Merged
fix(ui): prevent concurrent form submissions on double-click [#2982]#2983viniciusdacal merged 1 commit intomainfrom
viniciusdacal merged 1 commit intomainfrom
Conversation
Double-clicking a submit button fired two submit events back-to-back. Both entered submitPipeline, passed validation, and called the SDK — creating duplicate records. submitting was only set to true after validation, and there was no reentrancy guard. submitPipeline now checks submitting.peek() synchronously at entry and returns early if a submission is already in flight. submitting.value = true is set before any work (so a second sync call sees the guard) and a try/finally guarantees it is reset on every exit path. The pipeline returns a boolean so onSubmit / submit wrappers skip their post-processing (form reset) when their call was rejected. Closes #2982 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
form()didn't lock against re-entrant submissions — double-clicking submit fired twosubmitevents, both enteredsubmitPipeline, passed validation, and called the SDK, creating duplicate records.submitPipelinenow checkssubmitting.peek()synchronously at entry and bails if already in flight;submitting.value = trueis set before any work; atry/finallyresets it on every exit path.booleansoonSubmit/submitwrappers skip their post-processing (form reset) when their call was rejected — prevents a stale doubleformElement.reset()on the losing click.Closes #2982
Public API Changes
None. Internal refactor of
submitPipeline. User-facing contract is unchanged — just fewer bugs.Test plan
packages/ui/src/form/__tests__/form-concurrent.test.ts— 4 new tests:submit()calls — SDK called once,submittingsettles back to false.onSubmit— SDK called once,onSuccessfired once, only the winning event'starget.reset()is invoked.@vertz/uitests pass.vtz run typecheckgreen.vtzx oxlint/vtzx oxfmtclean on changed files.Review
Adversarial self-review at
reviews/fix-2982/review.md(not committed — working artifact). Two blockers found and addressed: (1) outer wrappers would still reset the form on rejected calls, fixed by returning a boolean from the pipeline and gating post-processing; (2) validation-failure test used two different form instances so it didn't exercise the same-form lock release — rewrote to use a single form with a schema that flips between fail/pass.🤖 Generated with Claude Code