javascripthtmlreactjsform-submitclient-side-validation

How to use browser validation on submit without blocking the submit handler


HTML5 lets us submitting forms from a button outside the form.

<form id="myform">
  <input required/>
</form>
<button type="submit" form="myform"/>

This doesn't work well with React apparently as you can see in the following snippet.

const App = () => {
  const [value, setValue] = React.useState("");
  const [result, setResult] = React.useState("");

  const onSubmit = (e) => {
    e.preventDefault();
    if (!value) {
      setResult("Error");
    } else {
      setResult(value);
    }
  };

  return (
    <div className="box">
      <form id="myform" className="box" onSubmit={onSubmit}>
        <h3>My form</h3>
        <input
          className="input"
          value={value}
          required
          onChange={(e) => setValue(e.target.value)}
        />
      </form>
      <button type="submit" form="myform" className="button">
        Submit
      </button>
      <div>Result: {result}</div>
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("root"));
body {
  height: 100vh;
  margin: 0;
  display: grid;
  place-items: center;
}

.box {
  background-color: #fff;
  border-radius: 6px;
  box-shadow: 0 2px 3px rgb(10 10 10 / 10%), 0 0 0 1px rgb(10 10 10 / 10%);
  color: #4a4a4a;
  display: block;
  padding: 1.25rem;
  margin-bottom: 1.5rem;
}

.button {
  border: 1px solid transparent;
  border-radius: 4px;
  font-size: 1rem;
  height: 2.25em;
  line-height: 1.5;
  background-color: #fff;
  border-color: #dbdbdb;
  border-width: 1px;
  color: #363636;
  cursor: pointer;
  justify-content: center;
  text-align: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>

I would expect the "Result" box to show "Error". Instead, the browser validation kicks in and the form submit handler is not called at all.

Am I doing something wrong?

Edit

Thanks to the comments to this post I realized that I based my question on a wrong assumption: that having the submit button outside a (invalid) form was causing the browser to prevent the execution of the submit handler. Instead, this is just the normal behavior of any form with validation and a submit handler.

So, if we wanted to salvage this question, this is what I would like to achieve on form submit:

I worked around it by using a simple click handler on the button instead of the submit handler on the form. But it would be nice to still use the submit button and handler and just disable the browser behaviours to prevent the submit handler execution and show the balloon messages.


Solution

  • I found out about the existence of the invalid event. It is raised when a form submission is attempted with invalid data. This event's default can be prevented to avoid the browser showing the default warning balloons. The logic for showing the validation errors can be implemented in the invalid event handler and let the submit handler only worry about submitting the validated data.

    <form onInvalid={e => {
      e.preventDefault()
      // show validation errors
    } onSubmit={e => {
      e.preventDefault()
      // Submit data
    }>
      <input required/>
      <button type="submit"/>
    </form>