Skip to content

Commit edc9e6a

Browse files
lpssformspreeclaude
andcommitted
Fix race condition in form submission and error handling in queue flush
Move disable/clear calls before async data resolution to prevent duplicate submissions when ExtraData contains async functions. Add per-form submitting guard to block concurrent handleSubmit calls. Wrap queue flush loop in try-catch so one failing form doesn't prevent remaining forms from initializing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 7099f25 commit edc9e6a

File tree

2 files changed

+21
-10
lines changed

2 files changed

+21
-10
lines changed

packages/formspree-ajax/src/form.ts

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,12 @@ import {
2323
} from './utils';
2424

2525
const handleSubmit = async <T extends FieldValues>(
26-
context: FormContext<T>
26+
context: FormContext<T>,
27+
guard: { submitting: boolean }
2728
): Promise<void> => {
29+
if (guard.submitting) return;
30+
guard.submitting = true;
31+
2832
const { formKey, endpoint, client, config } = context;
2933
const {
3034
debug,
@@ -40,6 +44,13 @@ const handleSubmit = async <T extends FieldValues>(
4044
renderFormError = defaultRenderFormError,
4145
} = config;
4246

47+
// Clear visible errors and messages before submitting
48+
renderFieldErrors(context, null);
49+
renderSuccess(context, null);
50+
renderFormError(context, null);
51+
disable(context);
52+
onSubmit?.(context);
53+
4354
const formData = new FormData(context.form);
4455

4556
if (data) {
@@ -56,13 +67,6 @@ const handleSubmit = async <T extends FieldValues>(
5667
}
5768
}
5869

59-
// Clear visible errors and messages before submitting
60-
renderFieldErrors(context, null);
61-
renderSuccess(context, null);
62-
renderFormError(context, null);
63-
disable(context);
64-
onSubmit?.(context);
65-
6670
if (debug) {
6771
log('Submitting form', { formKey, formData });
6872
}
@@ -104,6 +108,7 @@ const handleSubmit = async <T extends FieldValues>(
104108
renderFormError(context, 'An unexpected error occurred. Please try again.');
105109
onFailure?.(context, err);
106110
} finally {
111+
guard.submitting = false;
107112
enable(context);
108113
}
109114
};
@@ -144,9 +149,11 @@ export const initForm = <T extends FieldValues = FieldValues>(
144149
log('Initializing form', context);
145150
}
146151

152+
const guard = { submitting: false };
153+
147154
const submitHandler = (event: Event): void => {
148155
event.preventDefault();
149-
handleSubmit(context);
156+
handleSubmit(context, guard);
150157
};
151158

152159
form.addEventListener('submit', submitHandler);

packages/formspree-ajax/src/global.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ window.formspree = (...args: unknown[]) => {
2727
// Flush queued calls once the DOM is ready
2828
onReady(() => {
2929
for (const args of queue) {
30-
run(args[0], args[1]);
30+
try {
31+
run(args[0], args[1]);
32+
} catch (err) {
33+
console.error('[formspree] Failed to initialize form from queue:', err);
34+
}
3135
}
3236
});

0 commit comments

Comments
 (0)