issue: event.stopPropagation() does not work in callback passed to handleSubmit
Version Number
7.43.2
Codesandbox/Expo snack
https://codesandbox.io/s/snowy-https-hnyckh?file=/src/App.tsx
Steps to reproduce
- Go to https://codesandbox.io/s/snowy-https-hnyckh?file=/src/App.tsx
- Click on 'Show modal'
- Enter an e-mail address in the modal form
- Click the 'Submit' button on the modal
- Observe that the outer form submit count increases and its onSubmit handler is called.
Expected behaviour
The outer form should not be submitted as the Modal component calls event.stopPropagation() in the callback passed to handleSubmit.
What browsers are you seeing the problem on?
Chrome
Relevant log output
in <Modal /> onSubmit:
{email: "[email protected]"}
in top form onSubmit:
{email: "", firstName: ""}
Code of Conduct
- [X] I agree to follow this project's Code of Conduct
From looking at the source in createFormControl.ts this seems to be because the onValid callback is called after an await point: https://github.com/react-hook-form/react-hook-form/blob/f4577b7d79810b6631d4f665ae7acbea5c90d00f/src/logic/createFormControl.ts#L1055
So the onSubmit handler will be suspended before it reaches the stopPropagation() call (and presumably since react doesn't await on async event handlers, the control flow to bubble up the event still occurs). I couldn't find any documentation on the react-hook-form site covering this behavior, but apologies if I overlooked something.
hey, @c-t-k we do support async callback within the handleSubmit, eg post a request. It's async by default. I will fix the type description in the doc.
hey, @c-t-k we do support async callback within the
handleSubmit, eg post a request. It's async by default. I will fix the type description in the doc.
@bluebill1049 I think you might misunderstand me. I understand you can pass an async callback to handleSubmit(), that isn't the issue. It's that calling stopPropagation() on a synthetic event in any callback you pass to handleSubmit() will not work.
See the codesandbox I posted above to see where this is going wrong. The Modal component should prevent a submit event from bubbling up to the top level form, as it uses this as the onSubmit handler on the form it manages:
const onSubmit = handleSubmit((value, ev) => {
ev?.stopPropagation();
onSubmitCallback(value);
onClose();
});
However you can see that when the modal's form is submitted, it ends up triggering the outer form despite stopPropagation() being called.
I believe this is because react invokes an onSubmit() handler as though it is a synchronous function, and handleSubmit has an await point in its own code which will cause execution to suspend before the stopPropagation() can be reached (before the onValid() callback you pass is await-ed in the body of handleSubmit().
Yes, @c-t-k I understand that, what's your prospered change, I don't think we can remove the await for the onValid callback. can i suggest the following
onSubmit=((e) => {
// e.
handleSubmit(() => {})(e)
})
@bluebill1049 yeah, this is the workaround I've been using:
<form onSubmit={((e) => {
e.stopPropagation();
form.handleSubmit(onSubmitCallback)(e)
})} />
I don't have a proposed fix, it seems to be a problem w/ the design of handleSubmit that it passes the event to onValid and onInvalid though. Since those callbacks can't affect event bubbling at that point, it seems like a design decision that's bound to create surprises. I'd at least suggest making a note about this in your documentation for the time being so that users won't be caught off guard by this behavior.
https://react-hook-form.com/api/useform/handlesubmit/
Would you like to send us a PR on this? We can describe and move it under the rules section.
@c-t-k Could you try this -
Any progress on this? Might be nice to be able to have a property in useForm hook stopPropagation: boolean?
Hi All, i was working on this issue, this approach is working: if we remove the form from child or simple apply event handler on button itself.
const onSubmit = handleSubmit((value, ev) => { ev?.preventDefault() ev?.stopPropagation(); onSubmitCallback(value); onClose(); });
please let me know if you need more clarity. Thanks
CC - @c-t-k
@c-t-k could you please assign this to me.
Can someone assign this to me, if it is not resolved
This is similar to the other answers but I'll post my solution here:
function stopPropagate(callback: (event: FormEvent<HTMLFormElement>) => void) {
return (e: FormEvent<HTMLFormElement>) => {
e.stopPropagation();
callback(e);
};
}
<form onSubmit={stopPropagate(handleSubmit(onSubmit))}>