React

React Form

form

There is a difference between HTML form elements and other regular DOM elements in React. Other dom element can be either stateful or stateless depending upon your implementation, but form elements maintain their state.

Let’s consider a simple html form

<form>
  <label>
    Email:
    <input type="email" name="Your Email" />
  </label>
  <input type="submit" value="Submit" />
</form>

When you submit this form, it will work as a usual html form with all data going to server as request parameters (here we just have one field called email). But in general we implement a javascript callback function on a form submit which has access to all form variables. So how to get this functionality in React. It is through a concept called Controlled components.

Controlled Component react

In HTML, various form elements (<input>, <select>, <textarea>) maintain their own state (the values that user enters/changes). In React any change in value is kept in state variables and updated with setState method. In React, we combine these two states and make React State as system of record.

So now an input form element whose value is controlled by React state is known as Controlled Component.

Now the below form has input of type email as controlled component.

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

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

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

  handleSubmit(event) {
    alert('An email was submitted: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="email" value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

You can view it live here https://codepen.io/anilpank/pen/LYYpRrx

The value attribute of the input type email is set to value variable of state. So whenever the user types something, onchange event will be triggerred which will set the state variable value, which will in turn set value attribute of input DOM element. If some other method sets value state variable to x@y.com, then input DOM element’s value will be set to x@y.com.

In a controlled component, you will always have a onchange event handler callback function. So this makes it easy to manipulate user input.

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

React Select Tag

First let’s see the html select example.

<select>
  <option value="cauliflower">Cauliflower</option>
  <option value="brinjal">Brinjal</option>
  <option selected value="cabbage">Cabbage</option>
  <option value="potato">Potato</option>
</select>

Here Cabbage option is initially selected because of selected attribute. The React Select tag differs here. As it uses value attribute (on the root select tag) instead of selected attribute on individual option tag. See the example code below.

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

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

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

  handleSubmit(event) {
    alert('Your favorite vegetable is: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Pick your favorite vegetable:
          <select value={this.state.value} onChange={this.handleChange}>
            <option value="cauliflower">Cauliflower</option>
  <option value="brinjal">Brinjal</option>
  <option selected value="cabbage">Cabbage</option>
  <option value="potato">Potato</option>
          </select>
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

You can see it live here. https://codepen.io/anilpank/pen/dyyYWoJ

The three form elements <input type=”text”>, <select> and <textarea> accept value attribute that can be used to implement controlled components.

React Multiselect

React multiselect is very similar to single select, except for one difference. Here you will pass an array of multiple values to value attribute. You also need to set multiple attribute to true.

<select multiple={true} value={['Fan', 'Light']}>

File Input

In regular html, an <input type=”file”> can have user upload one more more files which can be handled at server or manipulated by the javascript File api.

<input type="file" />

In React you can’t create a controlled component for this since this input is read only and can’t be changed by state variable. This is an uncontrolled component. I’ll create a separate blog post for this.

Form with multiple input fields

Typically a form that you would create will have multiple input fields. So creating a change callback function for each of them would be very tedious. In order to avoid one function per input field, we can have a name attribute for each input and then utilize that name to figure out what to do in a single callback function.

Check the code below to see the example.

class Flight extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isBoarding: true,
      numberOfGuests: 2
    };

    this.handleInputChange = this.handleInputChange.bind(this);
  }

  handleInputChange(event) {
    const target = event.target;
    const value = target.type === 'checkbox' ? target.checked : target.value;
    const name = target.name;

    this.setState({
      [name]: value
    });
  }

  render() {
    console.log('isBoarding value:', this.state.isBoarding);
    let message;
    if (this.state.isBoarding) {
       message = 'We have ' + this.state.numberOfGuests + ' guests boarded';
    }
    else {
      message = 'The ' + this.state.numberOfGuests + ' guests are checked in';
    }
    return (
      <form>
        <div>{message}</div>
        <label>
          Is boarding:
          <input
            name="isBoarding"
            type="checkbox"
            checked={this.state.isBoarding}
            onChange={this.handleInputChange} />
        </label>
        <br />
        <label>
          Number of guests:
          <input
            name="numberOfGuests"
            type="number"
            value={this.state.numberOfGuests}
            onChange={this.handleInputChange} />
        </label>
      </form>
    );
  }
}

See it live here. https://codepen.io/anilpank/pen/wvvKEpj

We are using ES6 computed property names in order to set state.

this.setState({
  [name]: value
});

You can also use regular javascript ES5 to achieve that.

var limitedState = {};
limitedState[name] = value;
this.setState(limitedState);

React merges partial state into full state. So you only need to address changed parts.

Providing value property without onchange

If you specify a value prop on a controlled component, then you won’t be able to change the value through UI.

ReactDOM.render(
  <input value='abc'/>,
  document.getElementById('root')
);

And you will get a warning like this.

Warning: Failed prop type: You provided a value prop to a form field without an onChange handler. This will render a read-only field. If the field should be mutable use defaultValue. Otherwise, set either onChange or readOnly.

So in general always have value attribute point to a state variable and bind an onchange callback function using controlled components.

If the input value is set to null or undefined, you can still edit the value in UI, but you will get a warning.

ReactDOM.render(
  <input value={null}/>,
  document.getElementById('root')
);

value prop on input should not be null. Consider using an empty string to clear the component or undefined for uncontrolled components.
in input

When not to use controlled components

In case you are migrating an existing application to React which has a lot of forms, then it could become very tedious to write every element, bind event handler, set state variable to it’s value and so on. In those cases we need not use controlled components and instead use the uncontrolled components. I’ll write a detailed blog post about it in some time.

How useful was this post?

Click on a star to rate it!

Average rating / 5. Vote count:

As you found this post useful...

Follow us on social media!

We are sorry that this post was not useful for you!

Let us improve this post!

About anil

Hi, I'm Anil Verma. I have a passion for teaching and developing awesome front end apps. I was involved in development of post processor for Nastran(built by NASA). More recently I have been building supply chain apps at E2open.
View all posts by anil →

Leave a Reply

Your email address will not be published. Required fields are marked *