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);
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;
}