i want to validate my form by checking if all the fields are filled. I am quite new to react. Any suggestions? I have got the states ready, but i am pretty much stuck. Thank you in advance for your replies. I appreciate it
my code:
const [fields, setFields] = useState({
name: '',
email: '',
subject: '',
msg: '',
});
const handleSubmit = (event) => {
emailjs.sendForm().then(
(result) => {
console.log(result.text);
},
(error) => {
console.log(error.text);
}
);
alert('form submitted');
event.target.reset();
event.preventDefault();
};
<form onSubmit={handleSubmit}>
<div className={styles.firstInputs}>
<div>
<label>name</label>
<br />
<input
type='text'
refs='name'
name='name'
placeholder='your name'
></input>
</div>
<div>
<label>email</label>
<br />
<input type='email' name='email' placeholder='email'></input>
</div>
</div>
<label>subject</label>
<br />
<input
type='text'
name='subject'
placeholder='subject'
className={styles.subject}
></input>{' '}
<br />
<label>message</label> <br />
<textarea placeholder='your message' name='message'></textarea>
<br />
<button type='submit' className={styles.sendForm}>
SUBMIT
</button>
</form>;
You are not using your state correctly there.
Set the values of the fields to what is in the state, then change the state when the fields change.
Don't forget you can just add the required attribute to your HTML element to make sure it gets some kind of value.
Then you just validate the state before submission.
Don't just fall back on packages all the time as people suggest here. As you say, you are new to React so, learn to set things up manually (which is basically less work for small forms). Once you understand that, then consider if having another dependency is really saving you time.
See sample code below in action here: https://codesandbox.io/s/form-validation-pbbf1
import { useState } from "react";
import "./styles.css";
const defaultFields = { name: "", email: "", subject: "", message: "" };
export default function App() {
const [fields, setFields] = useState(defaultFields);
const handleSubmit = (event) => {
// Stop form from resetting
event.preventDefault();
// Check fields
if (fields.name.length < 2) {
return alert("Name should be greater than 1 character.");
}
// etc...
// Send off data
/*
emailjs.sendForm().then(
(result) => {
console.log(result.text);
},
(error) => {
console.log(error.text);
}
);
*/
// I would probably put these two
// lines inside the then block above.
setFields(defaultFields);
alert("form submitted");
};
const handleFieldChange = (e) => {
const { name, value } = e.target;
setFields((previousFields) => ({ ...previousFields, [name]: value }));
};
return (
<form onSubmit={handleSubmit}>
{/* NAME */}
<div>
<label>name</label>
<br />
<input
type="text"
name="name"
placeholder="your name"
value={fields.name}
required
onChange={handleFieldChange}
></input>
</div>
{/* EMAIL */}
<div>
<label>email</label>
<br />
<input
type="email"
name="email"
placeholder="email"
value={fields.email}
required
onChange={handleFieldChange}
></input>
</div>
{/* SUBJECT */}
<div>
<label>subject</label>
<br />
<input
type="text"
name="subject"
placeholder="subject"
value={fields.subject}
required
onChange={handleFieldChange}
></input>{" "}
<br />
</div>
{/* MESSAGE */}
<div>
<label>message</label> <br />
<textarea
placeholder="your message"
name="message"
value={fields.message}
required
onChange={handleFieldChange}
></textarea>
</div>
<button type="submit">SUBMIT</button>
</form>
);
}
I am learning react and I have decided to create a React Front End that connects to Express.js on back-end. At the moment, I am trying to add validation on my register/login forms. I dont really understand how the validation system is implemented. I've noticed that tutorials use functional components for Formik/Yup however, I am using a class based component.
My register page doesn't display anything when i try to render it. Of course, I must have implemented this incorrectly, I cant figure out what to do. Thank you for help.
import React, { Component } from "react";
import { register } from "./UserFunctions";
import { Formik, Field } from "formik";
import * as Yup from "yup";
// const validationSchema = yup.object().shape({
// first_name: yup.string
// })
class Register extends Component {
constructor() {
super();
this.state = {
first_name: "",
last_name: "",
email: "",
password: "",
errors: {},
};
this.onChange = this.onChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
}
onChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
onSubmit(e) {
e.preventDefault();
//new user object
const newUser = {
first_name: this.state.first_name,
last_name: this.state.last_name,
email: this.state.email,
password: this.state.password,
};
//if register function succesful, redirect to login page
register(newUser).then((res) => {
this.props.history.push(`/login`);
});
}
render() {
return (
<div className="container">
<div className="row">
<div className="col-md-6 mt-5 mx-auto">
<Formik
initialValues={{
first_name: "",
last_name: "",
email: "",
password: "",
}}
validationSchema={Yup.object().shape({
first_name: Yup.string()
.required("First Name is Required.")
.min(1, "First Name is Too Short."),
last_name: Yup.string()
.required("Last Name is Required.")
.min(1, "Last Name is Too Short."),
email: Yup.string().email().required("Email is Required."),
password: Yup.string()
.required("No password provided.")
.min(8, "Password is too short - should be 8 chars minimum.")
.matches(/(?=.*[0-9])/, "Password must contain a number."),
})}
>
{(props) => {
const {
touched,
errors,
isSubmitting,
handleChange,
handleBlur,
handleSubmit,
} = props;
<form noValidate onSubmit={this.onSubmit}>
<h1 className="h3 mb-3 font-weight-normal">Register</h1>
<div className="form-group">
<label htmlFor="name">First name</label>
<input
type="text"
className="form-control"
name="first_name"
placeholder="Enter your first name"
value={this.state.first_name}
onChange={this.onChange}
/>
{errors.first_name && touched.first_name && (
<div className="input-feedback">{errors.first_name}</div>
)}
</div>
<div className="form-group">
<label htmlFor="name">Last name</label>
<input
type="text"
className="form-control"
name="last_name"
placeholder="Enter your last name"
value={this.state.last_name}
onChange={this.onChange}
/>
{errors.last_name && touched.last_name && (
<div className="input-feedback">{errors.last_name}</div>
)}
</div>
<div className="form-group">
<label htmlFor="email">Email address</label>
<input
type="email"
className="form-control"
name="email"
placeholder="Enter email"
value={this.state.email}
onChange={this.onChange}
/>
{errors.email && touched.email && (
<div className="input-feedback">{errors.email}</div>
)}
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<input
type="password"
className="form-control"
name="password"
placeholder="Password"
value={this.state.password}
onChange={this.onChange}
/>
{errors.password && touched.password && (
<div className="input-feedback">{errors.password}</div>
)}
</div>
<button
type="submit"
className="btn btn-lg btn-primary btn-block"
>
Register!
</button>
</form>;
}}
</Formik>
</div>
</div>
</div>
);
}
}
export default Register;
Formik validates the form when trying to submit, if there are any errors it doesn't allow the form to be submitted, it is upto you to display the error messages to the user when he tries to submit with errors see this page for reference
That is your code with functional component and with fewer code lines, it's working as expected
import React from "react";
import { useFormik } from "formik";
import { register } from "./UserFunctions";
import * as Yup from "yup";
const Register = ({ history }) => {
const validationSchema = Yup.object().shape({
first_name: Yup.string()
.required("First Name is Required.")
.min(1, "First Name is Too Short."),
last_name: Yup.string()
.required("Last Name is Required.")
.min(1, "Last Name is Too Short."),
email: Yup.string().email().required("Email is Required."),
password: Yup.string()
.required("No password provided.")
.min(8, "Password is too short - should be 8 chars minimum.")
.matches(/(?=.*[0-9])/, "Password must contain a number."),
});
const formik = useFormik({
initialValues: {
first_name: "",
last_name: "",
email: "",
password: "",
},
validationSchema: validationSchema,
onSubmit: (values) => {
const newUser = {
first_name: values.first_name,
last_name: values.last_name,
email: values.email,
password: values.password,
};
//if register function succesful, redirect to login page
register(newUser).then((res) => {
history.push(`/login`);
});
},
});
return (
<div className="container">
<div className="row">
<div className="col-md-6 mt-5 mx-auto">
<form noValidate onSubmit={formik.handleSubmit}>
<h1 className="h3 mb-3 font-weight-normal">Register</h1>
<div className="form-group">
<label htmlFor="name">First name</label>
<input
type="text"
className="form-control"
name="first_name"
placeholder="Enter your first name"
value={formik.values.first_name}
onChange={formik.handleChange}
/>
{formik.errors.first_name && formik.touched.first_name && (
<div className="input-feedback">{formik.errors.first_name}</div>
)}
</div>
<div className="form-group">
<label htmlFor="name">Last name</label>
<input
type="text"
className="form-control"
name="last_name"
placeholder="Enter your last name"
value={formik.values.last_name}
onChange={formik.handleChange}
/>
{formik.errors.last_name && formik.touched.last_name && (
<div className="input-feedback">{formik.errors.last_name}</div>
)}
</div>
<div className="form-group">
<label htmlFor="email">Email address</label>
<input
type="email"
className="form-control"
name="email"
placeholder="Enter email"
value={formik.values.email}
onChange={formik.handleChange}
/>
{formik.errors.email && formik.touched.email && (
<div className="input-feedback">{formik.errors.email}</div>
)}
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<input
type="password"
className="form-control"
name="password"
placeholder="Password"
value={formik.values.password}
onChange={formik.handleChange}
/>
{formik.errors.password && formik.touched.password && (
<div className="input-feedback">{formik.errors.password}</div>
)}
</div>
<button type="submit" className="btn btn-lg btn-primary btn-block">
Register!
</button>
</form>
;
</div>
</div>
</div>
);
};
export default Register;
First, you need not use state while using Form Libraries. The main purpose of using Form libraries is to encapsulate the state and reduce the boilerplate.
Formik, itself, is a container for your form state.
Please Note :- you should pass the values of the formik form data into the input fields by using values ( which contains your form data) and handleChange provided by formik
Formik takes onSubmit as a prop where you can get your form state after validation and then, here you can register for your new user.
Also, you can check the codesandbox link here.
import React, { Component } from "react";
// import { register } from "./UserFunctions";
import { Formik } from "formik";
import * as Yup from "yup";
const schema = Yup.object().shape({
first_name: Yup.string()
.required("First Name is Required.")
.min(1, "First Name is Too Short."),
last_name: Yup.string()
.required("Last Name is Required.")
.min(1, "Last Name is Too Short."),
email: Yup.string().email().required("Email is Required."),
password: Yup.string()
.required("No password provided.")
.min(8, "Password is too short - should be 8 chars minimum.")
.matches(/(?=.*[0-9])/, "Password must contain a number.")
});
class Register extends Component {
render() {
return (
<div className="container">
<div className="row">
<div className="col-md-6 mt-5 mx-auto">
<Formik
initialValues={{
first_name: "",
last_name: "",
email: "",
password: ""
}}
validationSchema={schema}
// tell the formik to validate onBlur
validateOnBlur
onSubmit={(values) => {
// here you have the access to the form data
// values.first_name, values_last_name, values_email, values_password
//if register function succesful, redirect to login page
// register(values).then((res) => {
// this.props.history.push(`/login`);
// })
}}
>
{(props) => {
const {
touched,
errors,
handleSubmit,
values,
handleChange,
handleBlur,
} = props;
return (
<form noValidate onSubmit={handleSubmit}>
<h1 className="h3 mb-3 font-weight-normal">Register</h1>
<div className="form-group">
<label htmlFor="name">First name</label>
<input
type="text"
className="form-control"
name="first_name"
placeholder="Enter your first name"
value={values.first_name}
onChange={handleChange}
onBlur={handleBlur}
/>
{errors.first_name && touched.first_name && (
<div className="input-feedback">
{errors.first_name}
</div>
)}
</div>
<div className="form-group">
<label htmlFor="name">Last name</label>
<input
type="text"
className="form-control"
name="last_name"
placeholder="Enter your last name"
value={values.last_name}
onChange={handleChange}
onBlur={handleBlur}
/>
{errors.last_name && touched.last_name && (
<div className="input-feedback">{errors.last_name}</div>
)}
</div>
<div className="form-group">
<label htmlFor="email">Email address</label>
<input
type="email"
className="form-control"
name="email"
placeholder="Enter email"
value={values.email}
onChange={handleChange}
onBlur={handleBlur}
/>
{errors.email && touched.email && (
<div className="input-feedback">{errors.email}</div>
)}
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<input
type="password"
className="form-control"
name="password"
placeholder="Password"
value={values.password}
onChange={handleChange}
onBlur={handleBlur}
/>
{errors.password && touched.password && (
<div className="input-feedback">{errors.password}</div>
)}
</div>
<button
type="submit"
className="btn btn-lg btn-primary btn-block"
>
Register!
</button>
</form>
);
}}
</Formik>
</div>
</div>
</div>
);
}
}
export default Register;
I'm setting up a signup form that displays errors below the input fields if the user makes a mistake. The way I have it setup right now, the form will add a div with the error below when the user tries to submit their info. My issue is that when there's an error, it adds the div and messes up the layout of the form because it has to move everything to make space for each error. Is there a way to just have an empty div there if there isn't any errors so that it doesn't mess with the layout when there is one? So like, instead of having margin for spacing between fields, it's an empty div for the errors.
import React, { Component } from "react";
import axios from "axios";
import classnames from "classnames";
import "./Signup.css";
class Signup extends Component {
constructor() {
super();
this.state = {
username: "",
email: "",
password: "",
errors: {}
};
this.onChange = this.onChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
}
onChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
onSubmit(e) {
e.preventDefault();
const newUser = {
username: this.state.username,
email: this.state.email,
password: this.state.password
};
axios
.post("api/users/register", newUser)
.then(res => console.log(res.data))
.catch(err => this.setState({ errors: err.response.data }));
}
render() {
const { errors } = this.state;
return (
<div className="signup-form">
<form noValidate onSubmit={this.onSubmit}>
<h2>Sign Up</h2>
<p>It's free and only takes a minute.</p>
<hr />
<div className="form-group">
<label>Username</label>
<input
type="text"
name="username"
className={classnames("form-control form-control-md", {
"is-invalid": errors.username
})}
value={this.state.username}
onChange={this.onChange}
/>
{errors.username && (
<div className="invalid-feedback">{errors.username}</div>
)}
</div>
<div className="form-group">
<label>Email</label>
<input
type="text"
name="email"
className={classnames("form-control form-control-md", {
"is-invalid": errors.email
})}
value={this.state.email}
onChange={this.onChange}
/>
{errors.email && (
<div className="invalid-feedback">{errors.email}</div>
)}
</div>
<div className="form-group">
<label>Password</label>
<input
type="text"
name="password"
className={classnames("form-control form-control-md", {
"is-invalid": errors.password
})}
value={this.state.password}
onChange={this.onChange}
/>
{errors.password && (
<div className="invalid-feedback">{errors.password}</div>
)}
</div>
<div className="form-group">
<button type="submit" className="btn btn-primary btn-block btn-lg">
Sign Up
</button>
</div>
<p className="small text-center">
By clicking the Sign Up button, you agree to our <br />
Terms & Conditions, and{" "}
Privacy Policy
</p>
<div className="text-center">
Already have an account? Login here
</div>
</form>
</div>
);
}
}
export default Signup;
Yes, you can use visibility:hidden property of css.
<div style={{ visibility: error.email? 'visible': 'hidden'}}></div>
since visibility always takes up space, in both cases it is visible as well as hidden. so it won't mess with the layout.
I'm trying to make the invalid feedback validation disappear after being on the screen for 5 seconds. in my state i have an empty errors object, when the form is submitted, the api call catches any errors from the backend, and they are placed in the errors object, the inputs use conditionals based on the errors object to show the validation. I've tried creating a setTimeout function that sets the state to an empty object after 5 seconds, but this causes breaking glitches if the form is submitted again incorrectly. Any insights how I can do this?
Register.js
import React, { Component } from "react";
import axios from 'axios';
import classnames from 'classnames';
class Register extends Component {
constructor(props) {
super(props);
this.state = {
name: "",
email: "",
password: "",
password2: "",
errors: {}
};
this.onChange = this.onChange.bind(this);
}
onChange(e) {
// THIS FUNCTION MUST BE BOUND -SEE ABOVE BIND
this.setState({
[e.target.name]: e.target.value
});
}
onSubmit = e => {
// ARROW FUNCTIONS DO NOT NEED TO BE BOUND
e.preventDefault();
const newUser = {
name: this.state.name,
email: this.state.email,
password: this.state.password,
password2: this.state.password2
};
this.setState({
email: "",
name: "",
password: "",
password2: ""
});
axios
.post("/api/users/register", newUser)
.then(res => console.log(res.data))
.catch(err => this.setState({ errors: err.response.data }));
};
render() {
const { errors } = this.state;
return (
<div>
<div className="register">
<div className="container">
<div className="row">
<div className="col-md-8 m-auto">
<h1 className="display-4 text-center text-dark">Sign Up</h1>
<p className="lead text-center">Create your DevMuse account</p>
<form onSubmit={this.onSubmit}>
<div className="form-group">
<input
type="text"
className={classnames("form-control form-control-lg", {
"is-invalid": errors.name
})}
placeholder="Name"
name="name"
value={this.state.name}
onChange={this.onChange}
/>
{errors.name && (
<div className="invalid-feedback">{errors.name}</div>
)}
</div>
<div className="form-group">
<input
type="email"
className={classnames("form-control form-control-lg", {
"is-invalid": errors.email
})}
placeholder="Email Address"
name="email"
value={this.state.email}
onChange={this.onChange}
/>
{errors.email ? (
<div className="invalid-feedback">{errors.email}</div>
) : (
<small className="form-text text-muted text-center">
This site uses Gravatar so if you want a profile image,
use a Gravatar email
</small>
)}
</div>
<div className="form-group">
<input
type="password"
className={classnames("form-control form-control-lg", {
"is-invalid": errors.password
})}
placeholder="Password"
name="password"
value={this.state.password}
onChange={this.onChange}
/>
{errors.password && (
<div className="invalid-feedback">{errors.password}</div>
)}
</div>
<div className="form-group">
<input
type="password"
className={classnames("form-control form-control-lg", {
"is-invalid": errors.password2
})}
placeholder="Confirm Password"
name="password2"
value={this.state.password2}
onChange={this.onChange}
/>
{errors.password2 && (
<div className="invalid-feedback">{errors.password2}</div>
)}
</div>
<input
type="submit"
className="btn btn-info btn-block mt-4"
/>
</form>
</div>
</div>
</div>
</div>
</div>
);
}
}
export default Register;
Just clear any existing timeout before you initiate a new timeout:
componentWillUnmount() {
clearTimeout(this.clearError);
}
...
.catch((err) => {
this.setState({ errors: err.response.data });
clearTimeout(this.clearError); // clear previous timeout, if exists
this.clearError = setTimeout(() => {
this.setState({ errors: {} });
}, 5000);
});
My Contact page form is as follows,
<form name="contactform" onSubmit={this.contactSubmit.bind(this)}>
<div className="col-md-6">
<fieldset>
<input ref="name" type="text" size="30" placeholder="Name"/>
<br/>
<input refs="email" type="text" size="30" placeholder="Email"/>
<br/>
<input refs="phone" type="text" size="30" placeholder="Phone"/>
<br/>
<input refs="address" type="text" size="30" placeholder="Address"/>
<br/>
</fieldset>
</div>
<div className="col-md-6">
<fieldset>
<textarea refs="message" cols="40" rows="20"
className="comments" placeholder="Message"/>
</fieldset>
</div>
<div className="col-md-12">
<fieldset>
<button className="btn btn-lg pro" id="submit"
value="Submit">Send Message</button>
</fieldset>
</div>
</form>
Need to add validation for all fields. Can anyone help me to add validation in this react form?
You should avoid using refs, you can do it with onChange function.
On every change, update the state for the changed field.
Then you can easily check if that field is empty or whatever else you want.
You could do something as follows :
class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
fields: {},
errors: {},
};
}
handleValidation() {
let fields = this.state.fields;
let errors = {};
let formIsValid = true;
//Name
if (!fields["name"]) {
formIsValid = false;
errors["name"] = "Cannot be empty";
}
if (typeof fields["name"] !== "undefined") {
if (!fields["name"].match(/^[a-zA-Z]+$/)) {
formIsValid = false;
errors["name"] = "Only letters";
}
}
//Email
if (!fields["email"]) {
formIsValid = false;
errors["email"] = "Cannot be empty";
}
if (typeof fields["email"] !== "undefined") {
let lastAtPos = fields["email"].lastIndexOf("#");
let lastDotPos = fields["email"].lastIndexOf(".");
if (
!(
lastAtPos < lastDotPos &&
lastAtPos > 0 &&
fields["email"].indexOf("##") == -1 &&
lastDotPos > 2 &&
fields["email"].length - lastDotPos > 2
)
) {
formIsValid = false;
errors["email"] = "Email is not valid";
}
}
this.setState({ errors: errors });
return formIsValid;
}
contactSubmit(e) {
e.preventDefault();
if (this.handleValidation()) {
alert("Form submitted");
} else {
alert("Form has errors.");
}
}
handleChange(field, e) {
let fields = this.state.fields;
fields[field] = e.target.value;
this.setState({ fields });
}
render() {
return (
<div>
<form
name="contactform"
className="contactform"
onSubmit={this.contactSubmit.bind(this)}
>
<div className="col-md-6">
<fieldset>
<input
ref="name"
type="text"
size="30"
placeholder="Name"
onChange={this.handleChange.bind(this, "name")}
value={this.state.fields["name"]}
/>
<span style={{ color: "red" }}>{this.state.errors["name"]}</span>
<br />
<input
refs="email"
type="text"
size="30"
placeholder="Email"
onChange={this.handleChange.bind(this, "email")}
value={this.state.fields["email"]}
/>
<span style={{ color: "red" }}>{this.state.errors["email"]}</span>
<br />
<input
refs="phone"
type="text"
size="30"
placeholder="Phone"
onChange={this.handleChange.bind(this, "phone")}
value={this.state.fields["phone"]}
/>
<br />
<input
refs="address"
type="text"
size="30"
placeholder="Address"
onChange={this.handleChange.bind(this, "address")}
value={this.state.fields["address"]}
/>
<br />
</fieldset>
</div>
</form>
</div>
);
}
}
React.render(<Test />, document.getElementById("container"));
In this example I did the validation only for email and name, but you have an idea how to do it. For the rest you can do it self.
There is maybe a better way, but you will get the idea.
Here is fiddle.
Try this, example,
the required property in the below input tag will ensure that the name field shouldn't be submitted empty.
<input type="text" placeholder="Your Name" required />
I've taken your code and adapted it with library react-form-with-constraints: https://codepen.io/tkrotoff/pen/LLraZp
const {
FormWithConstraints,
FieldFeedbacks,
FieldFeedback
} = ReactFormWithConstraints;
class Form extends React.Component {
handleChange = e => {
this.form.validateFields(e.target);
}
contactSubmit = e => {
e.preventDefault();
this.form.validateFields();
if (!this.form.isValid()) {
console.log('form is invalid: do not submit');
} else {
console.log('form is valid: submit');
}
}
render() {
return (
<FormWithConstraints
ref={form => this.form = form}
onSubmit={this.contactSubmit}
noValidate>
<div className="col-md-6">
<input name="name" size="30" placeholder="Name"
required onChange={this.handleChange}
className="form-control" />
<FieldFeedbacks for="name">
<FieldFeedback when="*" />
</FieldFeedbacks>
<input type="email" name="email" size="30" placeholder="Email"
required onChange={this.handleChange}
className="form-control" />
<FieldFeedbacks for="email">
<FieldFeedback when="*" />
</FieldFeedbacks>
<input name="phone" size="30" placeholder="Phone"
required onChange={this.handleChange}
className="form-control" />
<FieldFeedbacks for="phone">
<FieldFeedback when="*" />
</FieldFeedbacks>
<input name="address" size="30" placeholder="Address"
required onChange={this.handleChange}
className="form-control" />
<FieldFeedbacks for="address">
<FieldFeedback when="*" />
</FieldFeedbacks>
</div>
<div className="col-md-6">
<textarea name="comments" cols="40" rows="20" placeholder="Message"
required minLength={5} maxLength={50}
onChange={this.handleChange}
className="form-control" />
<FieldFeedbacks for="comments">
<FieldFeedback when="*" />
</FieldFeedbacks>
</div>
<div className="col-md-12">
<button className="btn btn-lg btn-primary">Send Message</button>
</div>
</FormWithConstraints>
);
}
}
Screenshot:
This is a quick hack. For a proper demo, check https://github.com/tkrotoff/react-form-with-constraints#examples
import React from 'react';
import {sendFormData} from '../services/';
class Signup extends React.Component{
constructor(props){
super(props);
this.state = {
isDisabled:true
}
this.submitForm = this.submitForm.bind(this);
}
validateEmail(email){
const pattern = /[a-zA-Z0-9]+[\.]?([a-zA-Z0-9]+)?[\#][a-z]{3,9}[\.][a-z]{2,5}/g;
const result = pattern.test(email);
if(result===true){
this.setState({
emailError:false,
email:email
})
} else{
this.setState({
emailError:true
})
}
}
handleChange(e){
const target = e.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
if(e.target.name==='firstname'){
if(e.target.value==='' || e.target.value===null ){
this.setState({
firstnameError:true
})
} else {
this.setState({
firstnameError:false,
firstName:e.target.value
})
}
}
if(e.target.name==='lastname'){
if(e.target.value==='' || e.target.value===null){
this.setState({
lastnameError:true
})
} else {
this.setState({
lastnameError:false,
lastName:e.target.value
})
}
}
if(e.target.name==='email'){
this.validateEmail(e.target.value);
}
if(e.target.name==='password'){
if(e.target.value==='' || e.target.value===null){
this.setState({
passwordError:true
})
} else {
this.setState({
passwordError:false,
password:e.target.value
})
}
}
if(this.state.firstnameError===false && this.state.lastnameError===false &&
this.state.emailError===false && this.state.passwordError===false){
this.setState({
isDisabled:false
})
}
}
submitForm(e){
e.preventDefault();
const data = {
firstName: this.state.firstName,
lastName: this.state.lastName,
email: this.state.email,
password: this.state.password
}
sendFormData(data).then(res=>{
if(res.status===200){
alert(res.data);
this.props.history.push('/');
}else{
}
});
}
render(){
return(
<div className="container">
<div className="card card-login mx-auto mt-5">
<div className="card-header">Register here</div>
<div className="card-body">
<form id="signup-form">
<div className="form-group">
<div className="form-label-group">
<input type="text" id="firstname" name="firstname" className="form-control" placeholder="Enter firstname" onChange={(e)=>{this.handleChange(e)}} />
<label htmlFor="firstname">firstname</label>
{this.state.firstnameError ? <span style={{color: "red"}}>Please Enter some value</span> : ''}
</div>
</div>
<div className="form-group">
<div className="form-label-group">
<input type="text" id="lastname" name="lastname" className="form-control" placeholder="Enter lastname" onChange={(e)=>{this.handleChange(e)}} />
<label htmlFor="lastname">lastname</label>
{this.state.lastnameError ? <span style={{color: "red"}}>Please Enter some value</span> : ''}
</div>
</div>
<div className="form-group">
<div className="form-label-group">
<input type="email" id="email" name="email" className="form-control" placeholder="Enter your email" onChange={(e)=>{this.handleChange(e)}} />
<label htmlFor="email">email</label>
{this.state.emailError ? <span style={{color: "red"}}>Please Enter valid email address</span> : ''}
</div>
</div>
<div className="form-group">
<div className="form-label-group">
<input type="password" id="password" name="password" className="form-control" placeholder="Password" onChange={(e)=>{this.handleChange(e)}} />
<label htmlFor="password">Password</label>
{this.state.passwordError ? <span style={{color: "red"}}>Please enter some value</span> : ''}
</div>
</div>
<button className="btn btn-primary btn-block" disabled={this.state.isDisabled} onClick={this.submitForm}>Signup</button>
</form>
</div>
</div>
</div>
);
}
}
export default Signup;
With React Hook, form is made super easy (React Hook Form: https://github.com/bluebill1049/react-hook-form)
i have reused your html markup.
import React from "react";
import useForm from 'react-hook-form';
function Test() {
const { useForm, register } = useForm();
const contactSubmit = data => {
console.log(data);
};
return (
<form name="contactform" onSubmit={contactSubmit}>
<div className="col-md-6">
<fieldset>
<input name="name" type="text" size="30" placeholder="Name" ref={register} />
<br />
<input name="email" type="text" size="30" placeholder="Email" ref={register} />
<br />
<input name="phone" type="text" size="30" placeholder="Phone" ref={register} />
<br />
<input name="address" type="text" size="30" placeholder="Address" ref={register} />
<br />
</fieldset>
</div>
<div className="col-md-6">
<fieldset>
<textarea name="message" cols="40" rows="20" className="comments" placeholder="Message" ref={register} />
</fieldset>
</div>
<div className="col-md-12">
<fieldset>
<button className="btn btn-lg pro" id="submit" value="Submit">
Send Message
</button>
</fieldset>
</div>
</form>
);
}
Assuming you know about react useState Hook, If your form is simple, you can use state variables to hold the value of each input field. Then add onChange handler function on each input field which will update state variables. At the end, you can check the values stored in state variables to ensure that all the input fields had some value. Here is a simple example.
import { useState } from "react";
export default function App() {
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const onChangeHandler = (fieldName, value)=>{
if(fieldName === "name"){
setName(value);
}
else if(fieldName==="email"){
setEmail(value);
}
}
const onSubmitHandler = (e)=>{
e.preventDefault();
if(name.trim()==="" || email.trim() ==""){
alert("required both field");
}
else{
alert(name+" " +email);
setName("");
setEmail("");
}
}
return (
<div className="App">
<form onSubmit={(e)=>{onSubmitHandler(e)}}>
<input type="text" value={name} onChange={(e)=>{ onChangeHandler("name",e.target.value)}} /> <br/>
<input type="email" value={email} onChange={(e)=>{ onChangeHandler("email",e.target.value)}} /> <br/>
<input type="submit" value="Submit" />
</form>
</div>
);
}
However, if you are having a complex form, it's hard to keep each value in state variables and then use validation on each field. For complex forms, it is recommended to use Formik that will do everything for you and you can use Yup validation package which is also supported by Formik that will allow you to add more than just simple validation.
2022
React suggests 3 approaches to handle forms:
Controlled components - In HTML, form elements such as <input>, <textarea>, and <select> typically maintain their own state and update it based on user input. In React, mutable state is typically kept in the state property of components, and only updated with setState(). We can combine the two by making the React state be the “single source of truth”. Then the React component that renders a form also controls what happens in that form on subsequent user input. An input form element whose value is controlled by React in this way is called a “controlled component”.
Uncontrolled components - It can sometimes be tedious to use controlled components, because you need to write an event handler for every way your data can change and pipe all of the input state through a React component. This can become particularly annoying when you are converting a preexisting codebase to React, or integrating a React application with a non-React library. In these situations, you might want to check out uncontrolled components, an alternative technique for implementing input forms.
Fully-Fledged Solutions - If you’re looking for a complete solution including validation, keeping track of the visited fields, and handling form submission, Formik is one of the popular choices. However, it is built on the same principles of controlled components and managing state — so don’t neglect to learn them.
All approaches are valid to react hooks too.
First consider which component better suit your needs, and use its appropriate validation solution.
We have plenty of options to validate the react js forms. Maybe the npm packages have some own limitations. Based up on your needs you can choose the right validator packages. I would like to recommend some, those are listed below.
react-form-input-validation
redux-form
If anybody knows a better solution than this, please put it on the comment section for other people references.
Cleaner way is to use joi-browser package. In the state you should have errors object that includes all the errors in the form. Initially it shoud be set to an empty object.
Create schema;
import Joi from "joi-browser";
schema = {
username: Joi.string()
.required()
.label("Username")
.email(),
password: Joi.string()
.required()
.label("Password")
.min(8)
.regex(/^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9]).{8,1024}$/) //special/number/capital
};
Then validate the form with the schema:
validate = () => {
const options = { abortEarly: false };
const result = Joi.validate(this.state.data, this.schema, options);
console.log(data) // always analyze your data
if (!result.error) return null;
const errors = {};
for (let item of result.error.details) errors[item.path[0]] = item.message; //in details array, there are 2 properties,path and message.path is the name of the input, message is the error message for that input.
return errors;
};
Before submitting the form, check the form:
handleSubmit = e => {
e.preventDefault();
const errors = this.validate(); //will return an object
console.log(errors);
this.setState({ errors: errors || {} }); //in line 9 if we return {}, we dont need {} here
if (errors) return;
//so we dont need to call the server
alert("success");
//if there is no error call the server
this.dosubmit();
};
Might be late to answer - if you don't want to modify your current code a lot and still be able to have similar validation code all over your project, you may try this one too -
https://github.com/vishalvisd/react-validator.
Try powerform-react . It is based upon powerform which is a super portable Javascript form library. Once learnt, it can be used in any framework. It works even with vanilla Javascript.
Checkout this simple form that uses powerform-react
There is also a complex example.
Try this validation plugin in your form where you can add your custom validation rules.
Create a component FormValidation.js
import { useState } from "react";
const FormValidation = ({ validationRules, formInput }) => {
const [errors, setErrors] = useState(null);
const validation = () => {
// define a empty object to store errors.
let allErrors = {};
// Run loop on validation object
Object.keys(validationRules).forEach((name) => {
// name is the name of input field
const rulesArr = validationRules[name];
// Run loop on validation array applied on that input
rulesArr.forEach((rule) => {
// Skip if any error message is already stored in allErrors object
if (!allErrors[name]) {
let result;
// If rule is an array than it is a type of a function with parameter
switch (Array.isArray(rule)) {
case true: {
// take the function name and parameter value from rule array
const [functionName, paramValue] = rule;
// call validation function
result = functionName(formInput, name, paramValue);
break;
}
default:
// call validation function
result = rule(formInput, name);
break;
}
if (result) {
// append error in object
allErrors = { ...allErrors, ...result };
}
}
});
});
return allErrors;
};
const validate = () =>
new Promise((resolve, reject) => {
const errorObj = validation();
if (Object.keys(errorObj).length === 0) {
setErrors({});
resolve("Success");
} else {
setErrors(errorObj);
reject(Error("Some Error Occurred"));
}
});
return { validate, errors, setErrors };
};
export const required = (formInputs, inputName) =>
!formInputs[inputName] && { [inputName]: "This field is required" };
function emailPattern(email) {
return String(email)
.toLowerCase()
.match(
/^(([^<>()[\]\\.,;:\s#"]+(\.[^<>()[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
);
}
export const email = (formInputs, inputName) =>
!emailPattern(formInputs[inputName]) && {
[inputName]: "Please enter valid email",
};
export function passwordPattern(formInputs, inputName) {
const value = formInputs[inputName];
let error;
if (value.length < 8) {
error = "Your password must be at least 8 characters";
}
if (value.search(/[a-z]/i) < 0) {
error = "Your password must contain at least one letter.";
}
if (value.search(/[0-9]/) < 0) {
error = "Your password must contain at least one digit.";
}
if (value.search(/[ `!##$%^&*()_+\-=\[\]{};':"\\|,.<>\/?~]/) < 0) {
error = "Your password must contain at least one special character.";
}
return (
error && {
[inputName]: error,
}
);
}
export const maxLength = (formInputs, inputName, paramValue) =>
formInputs[inputName].length > paramValue && {
[inputName]: `Maximum characters are ${paramValue}`,
};
export const minLength = (formInputs, inputName, paramValue) =>
formInputs[inputName].length < paramValue && {
[inputName]: `Minimum characters are ${paramValue}`,
};
export default FormValidation;
I want to implement validations in my Login.js
import React, { useState } from "react";
import { Button, Form } from "react-bootstrap";
import FormValidation, {
required,
email,
passwordPattern,
} from "utils/FormValidation";
const Login = () => {
const [formInput, setFormInput] = useState({
email: "",
password: "",
});
const { validate, errors, setErrors } = FormValidation({
validationRules: {
email: [required, email],
password: [required, passwordPattern],
},
formInput,
});
const handleChange = (e) => {
setFormInput({ ...formInput, [e.target.name]: e.target.value });
};
const handleSubmit = (e) => {
e.preventDefault();
validate().then(() => {
//do whatever you want
console.log(formInput);
// you can set server error manually
setErrors({ email: "Email already exist" });
});
};
return (
<section className="gradient-form" style={{ backgroundColor: "#eee" }}>
<div className="container py-5 h-100">
<div className="row d-flex justify-content-center align-items-center h-100">
<div className="col-xl-10">
<div className="card rounded-3 text-black">
<div className="row g-0">
<div className="col-lg-6">
<div className="card-body p-md-5 mx-md-4">
<Form onSubmit={handleSubmit}>
<p>Please login to your account</p>
<Form.Group className="mb-3" controlId="formBasicEmail">
<Form.Control
type="text"
name="email"
placeholder="Enter email"
onChange={handleChange}
/>
{errors?.email && <span>{errors.email}</span>}
</Form.Group>
<Form.Group
className="mb-3"
controlId="formBasicPassword"
>
<Form.Control
type="password"
name="password"
placeholder="Password"
onChange={handleChange}
/>
{errors?.password && <span>{errors.password}</span>}
</Form.Group>
<Form.Group
className="mb-3"
controlId="formBasicCheckbox"
>
<Form.Check type="checkbox" label="Check me out" />
</Form.Group>
<div className="d-grid gap-2 mb-3">
<Button
variant="primary"
type="submit"
className="gradient-custom-2"
size="md"
>
Submit
</Button>
</div>
</Form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
);
};
export default Login;
Now you can pass any custom function in your validation array :
const { validate, errors, setErrors } = FormValidation({
validationRules: {
email: [required, email],
password: [required, passwordPattern, customFunciton],
},
formInput,
});
const customFunciton = (formInputs, inputName) => ({
[inputName]: `This error is from my custom function`,
});
I would like to suggest the following library, it is not react-specific. However, it provides an easy syntax and handy methods to run form validation. This is the react validation example from the library's documentation https://www.simple-body-validator.com/react/validation-quickstart