Search code examples
javascriptreactjsreact-final-form

React final-form I want to handleSubmit only if all fields are valid


I have setup a contact form with react-final-form. The problem is that I don't really understand how to allow submitting form only when all fields are valid.

Right now my form does the data fetch and shows errors underneath the fields either when they are empty or when e-mail address is not valid.

As a base for my react-final-form I used code from this sandbox. Also I don't really understand what is the difference between onSubmit in <Form/> and onSumbit in <form>.

Question 1. : How to allow handleSubmit only if all Fields are valid?

Question 2. : What is the difference between both onSubmit properties?

Here is my code :

import React from 'react';
import { Form, Field } from "react-final-form";

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

const onSubmit = async values => {
  await sleep(300);
  window.alert(JSON.stringify(values, 0, 2));
};

export class ContactPage extends React.Component {
  constructor(props) {
    super(props);
    this.handleSubmit=this.handleSubmit.bind(this);
  }

handleSubmit(e) {
  e.preventDefault();
  console.log(this.firstName.value);

  //Setting up values for fetch body
  const firstName = this.firstName.value;
  const email = this.email.value;
  const subject = this.subject.value;
  const message = this.message.value;

  fetch('/contact/send', {
    method:'POST',
    headers:{
      'Accept':'application/json, text/plain, */*',
      'Content-Type':'application/json'
    },
    body:JSON.stringify({
      firstName:firstName,
      email:email,
      subject:subject,
      message:message,
    }),
  })
  .then(res=>res.json())
  .then(console.log('here'))
  .then(data=>console.log(data))
  .catch(err=>console.log(err));

//Cleaning up data from form
  this.firstName.value="";
  this.email.value="";
  this.subject.value="";
  this.message.value="";
};

 render() {
    return (
      <div className="contact">
        <div className="contact__form">
          <Form
      onSubmit={onSubmit}
      validate={values => {
        const errors = {};
        if (!values.firstName) {
          errors.firstName = "Required";
        }
        if (!values.subject) {
          errors.subject = "Required";
        }
        if (!values.email) {
          errors.email = "Required";
        } else if(!values.email.match( /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/)) {
          errors.email = "Please enter a valid e-mail adress"
        }
        if (!values.message) {
          errors.message = "Required";
        }
        return errors;
      }}
      render={({ handleSubmit, submitting, values}) => (
        <form onSubmit={this.handleSubmit}>
          <Field name="firstName">
            {({ input, meta }) => (
              <div>
                <input {...input}
                  type="text"
                  placeholder="Name"
                  ref={(ref) => {this.firstName = ref}}
                />
                {meta.error && meta.touched && <span>{meta.error}</span>}
              </div>
            )}
          </Field>
          <Field name="subject">
            {({ input, meta }) => (
              <div>
                <input {...input}
                  type="text"
                  placeholder="Subject"
                  ref={(ref)=>{this.subject = ref}}
                />
                {meta.error && meta.touched && <span>{meta.error}</span>}
              </div>
            )}
          </Field>
          <Field name="email">
            {({ input, meta }) => (
              <div>
                <input {...input}
                  type="email"
                  placeholder="E-mail"
                  ref={(ref) => {this.email = ref}}
                />
                {meta.error && meta.touched && <span>{meta.error}</span>}
              </div>
            )}
          </Field>
          <Field name="message">
            {({ input, meta }) => (
              <div>
                <textarea {...input}
                  type="text"
                  placeholder="Message"
                  ref={(ref) => {this.message = ref}}
                />
                {meta.error && meta.touched && <span>{meta.error}</span>}
              </div>
            )}
          </Field>
          <div className="buttons">
            <button type="submit" disabled={submitting}>
              Submit
            </button>
          </div>
        </form>
      )}
    />
          </div>
      </div>
    );
  }
}

export default (ContactPage);

Solution

  • You are attaching your handleSubmit handled directly on the component which is getting called directly without checking the validations

    You should use onSubmit prop of the <Form> component to attach your handler like

     <Form onSubmit={this.handleSubmit}
    

    This will get passed down to the <form> component as handleSubmit that you can use as

     <form onSubmit={this.handleSubmit}>
    

    So your forms code will look like this:

    <Form
          onSubmit={this.handleSubmit}
          validate={values => {
            const errors = {};
            if (!values.firstName) {
              errors.firstName = "Required";
            }
            if (!values.subject) {
              errors.subject = "Required";
            }
            if (!values.email) {
              errors.email = "Required";
            } else if(!values.email.match( /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/)) {
              errors.email = "Please enter a valid e-mail adress"
            }
            if (!values.message) {
              errors.message = "Required";
            }
            return errors;
          }}
          render={({ handleSubmit, submitting, values}) => ( //handler gets passed here
            <form onSubmit={handleSubmit}> //don't use this. 
              <Field name="firstName">
                {({ input, meta }) => (
                  <div>
                    <input {...input}
                      type="text"
                      placeholder="Name"
                      ref={(ref) => {this.firstName = ref}}
                    />
                    {meta.error && meta.touched && <span>{meta.error}</span>}
                  </div>
                )}
              </Field>
              <Field name="subject">
                {({ input, meta }) => (
                  <div>
                    <input {...input}
                      type="text"
                      placeholder="Subject"
                      ref={(ref)=>{this.subject = ref}}
                    />
                    {meta.error && meta.touched && <span>{meta.error}</span>}
                  </div>
                )}
              </Field>
              <Field name="email">
                {({ input, meta }) => (
                  <div>
                    <input {...input}
                      type="email"
                      placeholder="E-mail"
                      ref={(ref) => {this.email = ref}}
                    />
                    {meta.error && meta.touched && <span>{meta.error}</span>}
                  </div>
                )}
              </Field>
              <Field name="message">
                {({ input, meta }) => (
                  <div>
                    <textarea {...input}
                      type="text"
                      placeholder="Message"
                      ref={(ref) => {this.message = ref}}
                    />
                    {meta.error && meta.touched && <span>{meta.error}</span>}
                  </div>
                )}
              </Field>
              <div className="buttons">
                <button type="submit" disabled={submitting}>
                  Submit
                </button>
              </div>
            </form>
          )}
        />
    

    Your handleSumit will not receive an events parameter insted it receives all the values in the form as a parameter so you can change it to

    handleSubmit(values){
    console.log(values)
    //You don't need the refs now
    const firstName = values.firstName;
    const email = values.email;
    const subject = values.subject;
    const message = values.message;
    
    }