Я использую React, у меня есть форма внутри другой формы:
Когда пользователь нажимает на определенную кнопку, появляется диалоговое окно с формой, которую я хочу отправить.
Мои две формы имеют разные идентификаторы, но когда я отправляю нужную мне форму (которая находится в диалоговом окне), форма в фоновом режиме также отправляется.
Я использую react-hook-form для управления своими формами, если это тоже имеет отношение.
Я надеюсь, что это изображение может дать небольшое представление о ситуации:
Я пытался:
onSubmitonSubmit в теге <form>, который хочу отправить.


![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Используйте event.stopPropagation() вне формы React Hook handleSubmit:
<form onSubmit = {( event ) => {
// Stop the event propagation OUTSIDE handleSubmit
event.stopPropagation();
// Explicitly pass the event
handleSubmit(submitHandlerFunction)(event);
}}>
<button type = "submit">Submit</button>
</form>
Форма React Hook handleSubmit запускает свой обратный вызов submitHandler после асинхронной проверки, поэтому даже если мы попытаемся использовать event.stopPropagation() внутри нашей submitHandlerFunction, уже слишком поздно предотвращать отправку «родительской» формы.
Вы можете видеть, что «родительская» форма отправляется до запуска нашего submitHandlerFunction:
function App() {
const { handleSubmit } = useForm();
function submitHandlerFunction(data, event) {
event.stopPropagation(); // Attempt stopping propagation, but it is already too late...
console.info('submitHandlerFunction called');
}
return (
<form
onSubmit = {(event) => {
event.preventDefault();
console.info('parent form submitted');
}}
>
<h2>Parent form</h2>
<Dialog>
<form onSubmit = {handleSubmit(submitHandlerFunction)}>
<h3>Child react-hook-form</h3>
<ol>
<li>Calls parent onSubmit</li>
<li>Calls child submitHandlerFunction</li>
</ol>
<button type = "submit">Submit child form</button>
</form>
</Dialog>
</form>
);
}
Живая демонстрация: https://stackblitz.com/edit/vitejs-vite-66e51m?file=src%2FApp.jsx
Нам нужно вызвать его снаружи handleSubmit:
<form
onSubmit = {(event) => {
// Stop the event propagation OUTSIDE handleSubmit
event.stopPropagation();
// Explicitly pass the event
handleSubmit(submitHandlerFunction)(event);
}}
>
<h3>Child react-hook-form stop propagation</h3>
<ol>
<li>Calls child submitHandlerFunction</li>
</ol>
<button type = "submit">Submit child form</button>
</form>
Мы также можем использовать некоторую библиотеку, чтобы немного упростить, например. Browser-event-utils withStopPropagation():
Вызывает
event.stopPropagation, затем передает событие предоставленной вами функции обработчика событий.
<form
onSubmit = {withStopPropagation(handleSubmit(submitHandlerFunction))}
>
<h3>Child react-hook-form withStopPropagation</h3>
<ol>
<li>Calls child submitHandlerFunction</li>
</ol>
<button type = "submit">Submit child form</button>
</form>
Как упоминалось Code Spirit в комментариях к вопросу, обычно <form> не должен быть вложен в другой <form>, согласно спецификации HTML:
Обратите внимание, что вам не разрешено вкладывать элементы FORM!
См. также Можно ли вкладывать HTML-формы?
При этом, поскольку ваш «дочерний элемент» <form> появляется в модальном диалоге, он, вероятно, отображается в DOM через React Portal (например, если вы используете MUI Dialog). В этом случае он больше не вложен в «родительскую» форму, поскольку Портал «отправляет» свой HTML-контент в другую часть дерева DOM (в случае диалога MUI — как последний дочерний элемент <body>).
Хотя сейчас мы технически соответствуем спецификации HTML, к сожалению, в React это все равно не будет работать из-за поведения порталов в отношении распространения событий:
Несмотря на то, что портал может находиться в любом месте дерева DOM, во всех остальных отношениях он ведет себя как обычный дочерний элемент React. [...]
Это включает в себя всплытие событий. Событие, запущенное изнутри портала, будет распространяться на предков в содержащем его дереве React, даже если эти элементы не являются предками в дереве DOM.
Итак, мы вернулись к исходной точке: действие "submit" запускает «дочернюю» и «родительскую» формы...
Существует также возможность связать элементы формы (здесь кнопка отправки) с явной формой, как также упоминается Code Spirit в комментариях к вопросу, используя для них атрибут form="formId":
Строка, определяющая элемент
<form>, с которым связаны входные данные (то есть владелец его формы). Значение этой строки, если оно присутствует, должно соответствоватьidэлемента<form>в том же документе. Если этот атрибут не указан, элемент<input>связан с ближайшей содержащей формой, если таковая имеется.Атрибут
formпозволяет разместить ввод в любом месте документа, но включить его в форму в другом месте документа.
Но это помогает, когда эти элементы находятся за пределами своего <form> (т. е. не являются потомками в дереве DOM). Это не нарушает нормальное распространение событий.
Как уже говорилось, решение состоит в том, чтобы остановить распространение события "submit", чтобы «родительская» форма не получала его и не запускала обратный вызов onSubmit.
Но с формой React Hook handleSubmit есть одна тонкость, см. пояснения выше.
Если вы не хотите отправлять другую форму, вы можете определить тип кнопки как кнопку для одной и отправить ее для другой.
<button type = "submit"></button>
<button type = "button"></button>
Формы не могут быть вложены в формы, но вы можете попробовать использовать
e.stopPropagation()в событии отправки вложенной формы и добавить атрибутformк кнопке отправки с идентификатором формы.