How do I add validation to the form in my React component?

Javascriptnode.jsReactjsFormsValidation

Javascript Problem Overview


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?

Javascript Solutions


Solution 1 - Javascript

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.

Hope this helps.

Solution 2 - Javascript

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 />

Solution 3 - Javascript

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:

form validation screenshot

This is a quick hack. For a proper demo, check https://github.com/tkrotoff/react-form-with-constraints#examples

Solution 4 - Javascript

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;

Solution 5 - Javascript

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

Solution 6 - Javascript

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

Solution 7 - Javascript

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.

If anybody knows a better solution than this, please put it on the comment section for other people references.

Solution 8 - Javascript

2022
React suggests 3 approaches to handle forms:

  1. 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”.

  2. 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.

  3. 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.

Solution 9 - Javascript

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.

Solution 10 - Javascript

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.

Solution 11 - Javascript

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.

Solution 12 - Javascript

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionHulk1991View Question on Stackoverflow
Solution 1 - JavascriptBokyView Answer on Stackoverflow
Solution 2 - JavascriptYash MarmatView Answer on Stackoverflow
Solution 3 - Javascripttanguy_kView Answer on Stackoverflow
Solution 4 - Javascriptuser10045612View Answer on Stackoverflow
Solution 5 - JavascriptBillView Answer on Stackoverflow
Solution 6 - JavascriptYilmazView Answer on Stackoverflow
Solution 7 - JavascriptGokulakannan TView Answer on Stackoverflow
Solution 8 - JavascriptGal MargalitView Answer on Stackoverflow
Solution 9 - JavascriptPreety SinghView Answer on Stackoverflow
Solution 10 - JavascriptAsurView Answer on Stackoverflow
Solution 11 - JavascriptSanan AliView Answer on Stackoverflow
Solution 12 - JavascriptamanView Answer on Stackoverflow