> Почему форма отправляется два раза при сабмите в React (React, JavaScript)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: Milk and Cartoons

Стек: React, JavaScript

> Пример ответа

Короткий ответ

Двойная отправка формы в React возникает из-за того, что обработчик submit вызывается дважды: один раз при клике на кнопку типа submit (событие click) и второй раз при отправке формы (событие submit). Это происходит, когда кнопка находится внутри формы и не вызван event.preventDefault() в обработчике submit, либо когда обработчик submit привязан и к форме, и к кнопке.

Подробное объяснение

В React форма может отправляться дважды по нескольким причинам. Основная - неправильная обработка событий. Когда пользователь кликает на <button type="submit"> внутри <form>, браузер генерирует два события: click на кнопке и submit на форме. Если оба события обрабатываются и каждый вызывает отправку данных (например, через fetch или axios), происходит двойной запрос.

Другая распространенная причина - использование onSubmit на форме вместе с onClick на кнопке без предотвращения распространения. В React события синтетические, но они все равно всплывают. Если обработчик onClick на кнопке вызывает event.stopPropagation(), это может не остановить submit событие формы, так как это разные события.

Также проблема может быть связана с Strict Mode в React 18, который дважды монтирует компоненты в development режиме для обнаружения побочных эффектов. Если эффект с подпиской на событие submit не очищается корректно, обработчик может быть зарегистрирован дважды.

Еще один сценарий - использование useEffect для подписки на событие submit без правильной очистки. При повторном рендере создается новая подписка, а старая не удаляется, что приводит к множественным вызовам.

На практике

Для предотвращения двойной отправки:

  • Всегда используйте event.preventDefault() в обработчике onSubmit формы
  • Не добавляйте обработчики onClick на кнопку submit, если уже есть onSubmit на форме
  • Если нужно обработать клик на кнопке отдельно, используйте type="button" вместо type="submit"
  • В useEffect всегда возвращайте функцию очистки для удаления подписок
  • Используйте флаг isSubmitting или disable кнопки после первого клика

Пример кода

JSX
// Проблемный код
function BadForm() {
const handleSubmit = (e) => {
// Нет preventDefault
fetch('/api/data', { method: 'POST', body: new FormData(e.target) });
};
const handleClick = () => {
fetch('/api/data', { method: 'POST' }); // Двойной запрос!
};
return (
<form onSubmit={handleSubmit}>
<button onClick={handleClick} type="submit">Отправить</button>
</form>
);
}
// Исправленный код
function GoodForm() {
const [isSubmitting, setIsSubmitting] = useState(false);
const handleSubmit = async (e) => {
e.preventDefault();
if (isSubmitting) return;
setIsSubmitting(true);
try {
await fetch('/api/data', { method: 'POST', body: new FormData(e.target) });
} finally {
setIsSubmitting(false);
}
};
return (
<form onSubmit={handleSubmit}>
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Отправка...' : 'Отправить'}
</button>
</form>
);
}

Как отвечать на собеседовании

Начните с основной причины - дублирование событий click и submit. Затем перечислите другие возможные причины: Strict Mode, неправильная очистка эффектов, отсутствие preventDefault. Приведите пример исправления с использованием флага isSubmitting и disable кнопки. Упомяните, что в production Strict Mode не влияет. Покажите понимание разницы между синтетическими и нативными событиями в React.

Что проверяет интервьюер

  • Понимание event loop и bubbling в DOM
  • Знание особенностей React SyntheticEvent
  • Умение работать с формами и предотвращать побочные эффекты
  • Понимание Strict Mode и его влияния на разработку
  • Навыки отладки и поиска причин дублирования запросов

Типичные ошибки

  • Использование onClick на кнопке submit вместе с onSubmit на форме
  • Отсутствие event.preventDefault() в обработчике submit
  • Игнорирование Strict Mode при тестировании
  • Неправильная очистка useEffect с подписками на события
  • Использование event.stopPropagation() вместо event.preventDefault()

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью