Как сделать, чтобы форма сабмитилась один раз и возвращала данные через функцию
Что проверяют
Умеете ли вы работать с событием submit, а не с кликом по кнопке, и понимаете ли, что двойную отправку ловят и на клиенте, и на сервере. Следом часто спрашивают, что будет, если запрос упал, и можно ли тогда отправить форму повторно.
Как отвечать
Обработчик вешается на submit формы, потому что форму отправляют и кнопкой, и клавишей Enter. Внутри event.preventDefault(), затем защита от повтора и возврат данных наружу:
- флаг или блокировка: переменная
isSubmittingилиdisabledна кнопке изevent.submitter. Пока запрос идёт, повторные нажатия игнорируются, после ответа флаг снимается вfinally; - данные через функцию: отправку удобно обернуть в функцию, которая возвращает
Promise. Внутриnew FormData(form)иfetch, наружу уходит результат. Вызывающий код пишетconst data = await submitOnce(form)или передаёт колбэк; - сбор полей:
Object.fromEntries(new FormData(form))удобен, но у чекбоксов и множественногоselectс одним именем теряет значения, там нуженformData.getAll(name); - отмена:
AbortControllerобрывает запрос, если пользователь ушёл со страницы; - React 19: состояние отправки дают
useActionStateиuseFormStatus, кнопку блокируют по признаку ожидания.
Отдельно скажите про сервер. Клиентская защита не спасает от повторов при плохой сети или от второй вкладки, поэтому для платежей и создания записей передают ключ идемпотентности, и сервер не выполняет одну операцию дважды.
Где спотыкаются
Пишут addEventListener('submit', handler, { once: true }). Обработчик снимается после первого вызова, следующее нажатие отправит форму нативно с перезагрузкой страницы, а после ошибки сети повторить отправку уже не выйдет.
Блокируют кнопку навсегда. Если запрос упал, пользователь застревает на форме без возможности исправить данные и отправить их снова.
Вызывают form.submit() для программной отправки. Он пропускает валидацию и не порождает событие submit, поэтому нужен form.requestSubmit().