React js form tutorial

Welcome to our new react js tutorial on “React JS Forms”. In this tutorial, we’re going to look at how React handles forms. We’ll cover not just the basics, but also form validations in React.

Forms are one of the few HTML elements that are interactive by default. They were designed to allow the user to interact with a page.

If you are new to react js tutorial, then check out my previous tutorial on, React js tutorial.

React js form

Let’s see some Common uses of forms:

  • Search
  • Contact forms
  • Booking the products
  • Login and registration and more!

There are two types of form input in react.

  • Uncontrolled Input and
  • Controlled Input.

Uncontrolled Input: If the data is handled by the DOM, we call them uncontrolled components

This is more like traditional HTML forms because the input form data is stored inside the DOM and not within the component. Elements like <input> and <textarea> maintain their own state, which they update when the input values change. You can query the DOM for the value of an input field using a ref.

Example:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.input = React.createRef();
  }

  handleSubmit(e) {
    alert('Text Entered in the TextBox is : ' + this.input.current.value);
    e.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
      <div>
      
        <label>
          Enter Text:
          <input type="text" ref={this.input} />
        </label></div><br></br>
        <div>
          
        <input type="submit" value="Submit" />
        </div>
      </form>
    );
  }
}
export default App;

Controlled Input: if the data is handled by the components we call them controlled components.

Example: Take the previous example, we can write that form as a controlled component.

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: ''};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(e) {
    this.setState({value: e.target.value});
  }

  handleSubmit(e) {
    alert('Text Entered in the TextBox is : ' + this.state.value);
    e.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
      <div>
        <label>
          Enter Text:
          <input type="text" value={this.state.value} onChange={this.handleChange} />
        </label></div><br></br>
        <div>
        <input type="submit" value="Submit" /></div>
      </form>
    );
  }
}
export default App;

The textarea Tag:

In React, a uses a value attribute to defines its text. This way, a form using a <textarea> can be written very similarly to a form that uses a single-line input.

Syntax:

<textarea />

Example:

class TextareaForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: 'Please enter text'
    };
}
handleChange(e) {
    //use the same code of the previous example
  }

  handleSubmit(e) {
    //use the same code of the previous example
  }
render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          MultipleLine Text:
          <textarea value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

The textarea component, takes the following props:

  • title: accepts a string that will be rendered in the textarea’s label.
  • rows: accepts an integer that determines how many rows high the textarea will be.
  • name: the name attribute for the textarea.
  • content: the content of the textarea. A controlled input will only display the data being passed into it via props.
  • resize: accepts a boolean that determines if the textarea will be resizable.
  • placeholder: a string that will be the textarea’s placeholder text.
  • controlFunc: is the function passed down from the parent/container component. This function will update the parent/container component’s state every time there is a change because it is attached to React’s an onChange handler.

Example:

<textarea
      className="form-input"
      name='Address'
      rows={props.rows}
      value={props.content}
      onChange={props.controlFunc}
      placeholder={props.placeholder} />

The select Tag:

Select components are used for collecting user-provided information from a list of options.

In HTML, uses a selected attribute on the select option tag. But in React, uses a value attribute on the root select tag. This is more convenient in a controlled component because you only need to update it in one place.

Syntax:

<select>
<option></option>
<option></option>
.................
................
</select>

Example:

class TextareaForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: 'India'
    };
}
handleChange(e) {
    //use the same code of the previous example
  }
handleSubmit(e) {
    //use the same code of the previous example
}
render(){ return(
<form onSubmit={this.handleSubmit}>
<label>
Select Country:
<select value={this.state.value} onChange={this.handleChange}>
 <option value="India">India</option>
 <option value="USA">USA</option>
 <option value="AUS">Aus</option>
 </select></lable>
</form>
);
}
}

The select component, takes the following props:

  • name: a string that will populate the name attribute of our form element.
  • options: an array (of strings in our case) in which each item will become an option by using props.options.map() in the component’s render method.
  • selectedOption: if we are prepopulating the form with either default data, or with data a user added in the past.
  • controlFunc: is the function passed down from the parent/container component. This function will update the parent/container component’s state every time there is a change because it is attached to React’s an onChange handler.
  • placeholder: a string that populates the first tag, and acts as placeholder text.

Example:

<select
      name={props.name}
      value={props.selectedOption}
      onChange={props.controlFunc}
      className="form-select">
      <option value="">{props.placeholder}</option>     
 </select>

The CheckBox tag:

Unlike the other components, the component takes in an array through its props, maps over the array and renders a set of form elements – either a set of checkboxes or a set of radios.

The CheckBox component, takes the following props:

title: a string that populates the label for the set of checkboxes/radios
type: takes one of two possible options, ‘checkbox’ or ‘radio’, and renders inputs of the indicated type.
setName: a string that will populate the name attributes of each checkbox/radio.
options: an array, in our case an array of strings, that determines the label and value for each checkbox/radio.
selectedOptions: an array, in our case an array of strings, of pre-selected options.
controlFunc: the function that handles adding and removing strings from the selectedOptions prop.

<input
       className="form-checkbox"
       name={props.setName}
       onChange={props.controlFunc}
       value={opt}
       checked={ props.selectedOptions.indexOf(opt) > -1 }

Example:

class Checkbox extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isChecked: true,
    };
  }
  toggleChange = () => {
    this.setState({
      isChecked: !this.state.isChecked,
    });
  }
  render() {
    return (
      <label>
        <input type="checkbox"
          checked={this.state.isChecked}
          onChange={this.toggleChange}
        />
        Check the checkbox!
       </label>
    );
  }
}

Validation types in React js form

When writing client side validations and validating a form there are two scopes that we need to be able to validate at on the client side. We need to be able to do simple field level validations and more complex form level validations.

Field level validation:

Field level validation is validating a single input, in isolation, for simple things like whether a field is required, whether a field’s length is under a maximum length or over a minimum, or whether a field satisfies a regular expression for things like emails, social security numbers, and so on.

Example:

Index.js:

import "./index.css";

function validate(email, password) {
    // true means invalid, so our conditions got reversed
    return {
      email: email.length === 0,
      password: password.length === 0
    };
  }
  
  class FieldValidation extends React.Component {
    constructor() {
      super();
      this.state = {
        email: "",
        password: "",
  
        touched: {
          email: false,
          password: false
        }
      };
    }
  
    handleEmailChange = evt => {
      this.setState({ email: evt.target.value });
    };
  
    handlePasswordChange = evt => {
      this.setState({ password: evt.target.value });
    };
  
    handleBlur = field => evt => {
      this.setState({
        touched: { ...this.state.touched, [field]: true }
      });
    };
  
    handleSubmit = evt => {
      if (!this.canBeSubmitted()) {
        evt.preventDefault();
        return;
      }
      const { email, password } = this.state;
      alert(`email: ${email} password: ${password}`);
    };
  
    canBeSubmitted() {
      const errors = validate(this.state.email, this.state.password);
      const isDisabled = Object.keys(errors).some(x => errors[x]);
      return !isDisabled;
    }
  
    render() {
      const errors = validate(this.state.email, this.state.password);
      const isDisabled = Object.keys(errors).some(x => errors[x]);
  
      const shouldMarkError = field => {
        const hasError = errors[field];
        const shouldShow = this.state.touched[field];
  
        return hasError ? shouldShow : false;
      };
  
      return (
        <form onSubmit={this.handleSubmit}>
        <lable>Email: </lable>
          <input
            className={shouldMarkError("email") ? "error" : ""}
            type="text"
            placeholder="Enter email"
            value={this.state.email}
            onChange={this.handleEmailChange}
            onBlur={this.handleBlur("email")}
          />
          <lable>Password:</lable>
          <input
            className={shouldMarkError("password") ? "error" : ""}
            type="password"
            placeholder="Enter password"
            value={this.state.password}
            onChange={this.handlePasswordChange}
            onBlur={this.handleBlur("password")}
          />
          <button disabled={isDisabled}>Submit</button>
        </form>
      );
    }
  }
  
  ReactDOM.render(<FieldValidation />, document.getElementById("root"));

index.css:

* {
  box-sizing: border-box;
}

body {
  padding-top: 0;
  font-family: Helvetica Neue, Helvetica;
  background: #f7f7f7;
}

h3 {
  font-size: 18px;
  margin-top: 20px;
  margin-bottom: 5px;
}

form {
  padding: 0 40px;
}

form input {
  display: block;
  width: 20%;
  font-size: 20px;
  padding: 5px 10px;
  margin: 10px 0;
  border-radius: 5px;
  border: 1px solid #ddd;
}

form input.error {
  border-color: red;
}

form button {
  display: block;
  width: 20%;
  appearance: none;
  border-radius: 5px;
  background-color: #0cc0af;
  color: #fff;
  border: none;
  font-size: 16px;
  height: 40px;
  margin-top: 30px;
}

form button:hover {
  background-color: #095695;
}

form button:disabled {
  background-color: #9fe0f9;
  color: #333;
}

output:

react form field validation
react form field validation

Form level validations in react js form

In addition to validating a form’s various fields against simple rules in isolation, we also need the ability to validate our form against complex rules that consider more than one field.

For example, we could have fields that are required when another field has a certain value, like requiring filling out a shipping address when it’s not the same as a billing address.

Example:

index.js:

function validate(name, email, password) {
    // we are going to store errors for all fields
    // in a signle array
    const errors = [];
  
    if (name.length === 0) {
      errors.push("Name can't be blank");
    }
  
    if (email.split("").filter(x => x === "@").length !== 1) {
      errors.push("Email should contain '@' ");
    }
    if (email.indexOf(".") === -1) {
      errors.push("Email should contain '.'");
    }
  
    if (password.length < 5) {
      errors.push("Password should be at least 5 characters long");
    }
  
    return errors;
  }
  
  class FormValidation extends React.Component {
    constructor() {
      super();
      this.state = {
        name: "",
        email: "",
        password: "",
  
        errors: []
      };
  
      this.handleSubmit = this.handleSubmit.bind(this);
    }
  
    handleSubmit(e) {
      e.preventDefault();
  
      const { name, email, password } = this.state;
  
      const errors = validate(name, email, password);
      if (errors.length > 0) {
        this.setState({ errors });
        return;
      }
  
      // submit the data...
    }
  
    render() {
      const { errors } = this.state;
      return (
        <form onSubmit={this.handleSubmit}>
          {errors.map(error => (
            <p key={error}>Error: {error}</p>
          ))}
          <lable>Name :</lable>
          <input
            value={this.state.name}
            onChange={evt => this.setState({ name: evt.target.value })}
            type="text"
            placeholder="Enter your name"
          />
          <lable>Email :</lable>
          <input
            value={this.state.email}
            onChange={evt => this.setState({ email: evt.target.value })}
            type="text"
            placeholder="Enter your email"
          />
          <lable>Password :</lable>
          <input
            value={this.state.password}
            onChange={evt => this.setState({ password: evt.target.value })}
            type="password"
            placeholder="Enter your password"
          />
  
          <button type="submit">Submit</button>
        </form>
      );
    }
  }
  
  ReactDOM.render(<FormValidation />, document.getElementById("root"));

index.css:

* {
  box-sizing: border-box;
}

body {
  padding-top: 0;
  font-family: Helvetica Neue, Helvetica;
  background: #f7f7f7;
}

h3 {
  font-size: 18px;
  margin-top: 20px;
  margin-bottom: 5px;
}

form {
  padding: 0 40px;
}

form input {
  display: block;
  width: 20%;
  font-size: 20px;
  padding: 5px 10px;
  margin: 10px 0;
  border-radius: 5px;
  border: 1px solid #ddd;
}
form button {
  display: block;
  width: 20%;
  appearance: none;
  border-radius: 5px;
/* background-color: #0cc0af;*/
background-color:#0cb1c0 ;
  color: #fff;
  border: none;
  font-size: 16px;
  height: 40px;
  margin-top: 30px;
}

form button:hover {
  background-color: #095695;
}

output:

react form validation
react form validation

react-validation-mixin

This library simply wraps your React Component, transferring it props containing the boilerplate to validate a React form.

react-validation-mixin aims to provide a low-level toolkit for React. Component validation, relying on existing validation libraries.

This library currently supports at least two strategies and the community is urged to expand the available strategies. Each strategy is responsible for data validation and error message responses. Users of the library are required to install and include the mixin and a chosen strategy.

Install mixin via npm:

npm install --save react-validation-mixin

Install validation strategy via npm:

npm install –save joi-validation-strategy

Make sure you install the peer dependency Joi if using the joi-validation-strategy:

npm install –save joi

Basic example for react-validation-mixin:

import React from 'react';
import Joi from 'joi';
import strategy from 'joi-validation-strategy';
import validation from 'react-validation-mixin';

const Demo = React.createClass({
  displayName: 'Demo',
  validatorTypes:  {
    name: Joi.string().required().label('Name'),
  },
  getValidatorData: function() {
    return this.state;
  },
  getInitialState: function() {
    return {
      name: null
    };
  },
  render: function() {
    return (
      <div>
        <label htmlFor='name'>Name</label>
        <input
          type='text'
          ref='name'
          placeholder='Enter Name'
          value={this.state.name}
          onChange={this.onChange('name')}
          onBlur={this.props.handleValidation('name')}
        />
        {this.renderHelpText(this.props.getValidationMessages('name'))}
      </div>
    )
  },
  renderHelpText: function(message) {
    return (
      <span className="help-block">{message}</span>
    );
  },
  onChange: function(field) {
    return event => {
      let state = {};
      state[field] = event.target.value;
      this.setState(state);
    };
  }
});

var ValidationDemo 
    = validation(strategy)(Demo);

ReactDOM.render(
    <ValidationDemo/>,
    document.getElementById("root"));

Basic React Form Example

Let us see now How to create form with react js using different controls.

index.html: Let’s suppose our index.html file has the following statement inside it:

<div id="root"></div>

Create new folder inside “src ” ->”components“. Inside This folder Create few componentes like Checkbox,Input,Text area,select,Button.

Button.jsx:

import React from "react";

const Button = props => {
  console.log(props.style);
  return (
    <button
      style={props.style}
      className={
        props.type == "primary" ? "btn btn-primary" : "btn btn-secondary"
      }
      onClick={props.action}
    >
      {props.title}
    </button>
  );
};

export default Button;

CheckBox.jsx:

import React from "react";

const CheckBox = props => {
  return (
    <div className="form-group">
      <label for={props.name} className="form-label">
        {props.title}
      </label>
      <div className="checkbox">
        {props.options.map(option => {
          return (
            <label key={option} className="checkbox-inline">
              <input
                id={props.name}
                name={props.name}
                onChange={props.handleChange}
                value={option}
                checked={props.selectedOptions.indexOf(option) > -1}
                type="checkbox"
              />
              {option}
            </label>
          );
        })}
      </div>
    </div>
  );
};

export default CheckBox;

Input.jsx:

import React from "react";

const Input = props => {
  //console.log(props.value);
  return (
    <div className="form-group">
      <label for={props.name} className="form-label">
        {props.title}
      </label>
      <input
        className="form-control"
        id={props.name}
        name={props.name}
        type={props.inputType}
        value={props.value}
        onChange={props.handleChange}
        placeholder={props.placeholder}
        {...props}
      />
    </div>
  );
};

export default Input;

Select.jsx:

import React from "react";

const Select = props => {
  return (
    <div className="form-group">
      <label for={props.name}> {props.title} </label>
      <select
        id={props.name}
        name={props.name}
        value={props.value}
        onChange={props.handleChange}
        className="form-control"
      >
        <option value="" disabled>
          {props.placeholder}
        </option>
        {props.options.map(option => {
          return (
            <option key={option} value={option} label={option}>
              {option}
            </option>
          );
        })}
      </select>
    </div>
  );
};

export default Select;

TextArea.jsx:

import React from "react";

const TextArea = props => (
  <div className="form-group">
    <label className="form-label">{props.title}</label>
    <textarea
      className="form-control"
      name={props.name}
      rows={props.rows}
      cols={props.cols}
      value={props.value}
      onChange={props.handleChange}
      placeholder={props.placeholder}
    />
  </div>
);
export default TextArea;

FormContainer.js: Import components Checkbox,Input,Text area,select,Button in FormContainer.js

import React, { Component } from "react";

/* Import Components */
import CheckBox from "./components/CheckBox";
import Input from "./components/Input";
import TextArea from "./components/TextArea";
import Select from "./components/Select";
import Button from "./components/Button";

class FormContainer extends Component {
  constructor(props) {
    super(props);

    this.state = {
      newUser: {
        name: "",
        contact: "",
        gender: "",
        skills: [],
        address: ""
      },

      genderOptions: ["Male", "Female", "Others"],
      skillOptions: ["SharePoint On-Premise", "Office 365", "SPFx"]
    };
    this.handleTextArea = this.handleTextArea.bind(this);
    this.handleContact = this.handleContact.bind(this);
    this.handleFullName = this.handleFullName.bind(this);
    this.handleFormSubmit = this.handleFormSubmit.bind(this);
    this.handleClearForm = this.handleClearForm.bind(this);
    this.handleCheckBox = this.handleCheckBox.bind(this);
    this.handleInput = this.handleInput.bind(this);
  }

  /* This lifecycle hook gets executed when the component mounts */

  handleFullName(e) {
    let value = e.target.value;
    this.setState(
      prevState => ({
        newUser: {
          ...prevState.newUser,
          name: value
        }
      }),
      () => console.log(this.state.newUser)
    );
  }

  handleContact(e) {
    let value = e.target.value;
    this.setState(
      prevState => ({
        newUser: {
          ...prevState.newUser,
          contact: value
        }
      }),
      () => console.log(this.state.newUser)
    );
  }

  handleInput(e) {
    let value = e.target.value;
    let name = e.target.name;
    this.setState(
      prevState => ({
        newUser: {
          ...prevState.newUser,
          [name]: value
        }
      }),
      () => console.log(this.state.newUser)
    );
  }

  handleTextArea(e) {
    console.log("Inside handleTextArea");
    let value = e.target.value;
    this.setState(
      prevState => ({
        newUser: {
          ...prevState.newUser,
          address: value
        }
      }),
      () => console.log(this.state.newUser)
    );
  }

  handleCheckBox(e) {
    const newSelection = e.target.value;
    let newSelectionArray;

    if (this.state.newUser.skills.indexOf(newSelection) > -1) {
      newSelectionArray = this.state.newUser.skills.filter(
        s => s !== newSelection
      );
    } else {
      newSelectionArray = [...this.state.newUser.skills, newSelection];
    }

    this.setState(prevState => ({
      newUser: { ...prevState.newUser, skills: newSelectionArray }
    }));
  }

  handleFormSubmit(e) {
    e.preventDefault();
    let userData = this.state.newUser;

    fetch("http://domain.com", {
      method: "POST",
      body: JSON.stringify(userData),
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json"
      }
    }).then(response => {
      response.json().then(data => {
        console.log("Successful" + data);
      });
    });
  }

  handleClearForm(e) {
    e.preventDefault();
    this.setState({
      newUser: {
        name: "",
        age: "",
        gender: "",
        skills: [],
        address: ""
      }
    });
  }

  render() {
    return (
      <form className="container-fluid" onSubmit={this.handleFormSubmit}>
        <Input
          inputType={"text"}
          title={"Full Name"}
          name={"name"}
          value={this.state.newUser.name}
          placeholder={"Enter your name"}
          handleChange={this.handleInput}
        />{" "}
        {/* Name of the user */}
        <Input
          inputType={"number"}
          name={"contact"}
          title={"Contact No"}
          value={this.state.newUser.contact}
          placeholder={"Enter your contact number"}
          handleChange={this.handleContact}
        />{" "}
        {/* Contact Number */}
        <Select
          title={"Gender"}
          name={"gender"}
          options={this.state.genderOptions}
          value={this.state.newUser.gender}
          placeholder={"Select Gender"}
          handleChange={this.handleInput}
        />{" "}
        {/* Gender Selection */}
        <CheckBox
          title={"Skills"}
          name={"skills"}
          options={this.state.skillOptions}
          selectedOptions={this.state.newUser.skills}
          handleChange={this.handleCheckBox}
        />{" "}
        {/* Skill */}
        <TextArea
          title={"Address "}
          rows={10}
          value={this.state.newUser.address}
          name={"currentPetInfo"}
          handleChange={this.handleTextArea}
          placeholder={"Enter your Address here"}
        />
        {/* Address */}
        <Button
          action={this.handleFormSubmit}
          type={"primary"}
          title={"Submit"}
          style={buttonStyle}
        />{" "}
        {/*Submit */}
        <Button
          action={this.handleClearForm}
          type={"secondary"}
          title={"Clear"}
          style={buttonStyle}
        />{" "}
        {/* Clear the form */}
      </form>
    );
  }
}

const buttonStyle = {
  margin: "10px 10px 10px 10px"
};

export default FormContainer;

Index.js: In this Import “formContainer.jsx” file here. Copy and paste this code in Index.js.

import React, { Component } from "react";
import { render } from "react-dom";
import FormContainer from "./FormContainer";

const styles = {
  fontFamily: "sans-serif",
  textAlign: "center"
};

class App extends Component {
  render() {
    return (
      <div className="col-md-6">
        <h3> Basic React Form  </h3>
        <FormContainer />
      </div>
    );
  }
}

render(<App />, document.getElementById("root"));

Index.css: Apply styling to the button and form.

*{
  box-sizing: border-box;
}

body {
  padding-top: 0;
  font-family: Helvetica Neue, Helvetica;
  background: #f7f7f7;
}

h3 {
  font-size: 18px;
  margin-top: 20px;
  margin-bottom: 5px;
}

form {
  padding: 0 40px;
}

form input {
  /* display: block; */
  /* width: 20%; */
  font-size: 20px;
  padding: 5px 10px;
  margin: 10px 0;
  border-radius: 5px;
  border: 1px solid #ddd;
}
form button {
  /* display: block; */
  width: 12%;
  appearance: none;
  border-radius: 5px;
 background-color:#0cb1c0 ;
  color: #fff;
  border: none;
  font-size: 16px;
  height: 40px;
  margin-top: 30px;
} 

 form button:hover {
  background-color: #095695;
}

output:

react form container example
react form container example

You may like following js tutorials:

This react js tutorial, we discuss, how to create forms, Field validations and form validations in react.

>