React Forms

Kay Ashaolu

Forms are a bit of a special case

  • Unlike other tags in HTML, forms keep some state
  • e.g. when you type text into a single line text field, that data is saved within the DOM
<input type="text" value="I just typed this" />
				 

Why is this a problem?

  • Remember that all state in React should be within components
  • This is how React knows to re-render components: when state changes
  • If state is left inside the DOM, React natively cannot react to any changes to it

One solution: Controlled Components

  • The idea is to tie form DOM state into React component state so that the two are updated simultaneously
  • After doing this wiring, you can now use your forms as if they were any other React component

Example

import React from "react";
import ReactDOM from "react-dom";

class NameForm 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.toUpperCase()});
 }

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

				

Example

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

ReactDOM.render(
  <NameForm />,
  document.getElementById('root')
);
				

Example Explained

  • Here we have an HTML form rendered using a React Component
  • We have a single state variable "value" that is set as the value of the single input on the form
  • We bind two events: onChange on the input value, and onSubmit on the form to handle changes to the text field and/or the form itself

Example Explained

  • We now can control the data of the form using this.state.value and can control how data is submitted using the handleSubmit function
  • Also note that modifying and validating input becomes as easy as modifying the handleChange function: this code only allows capital letters

The value property

  • Remember in HTML select tags needed the property "selected" to preselect a value
  • Or for the textarea tag the value was in between the start and end textarea tags?
  • In React to make things easier almost all form tags in JSX has a "value" property you can change
  • This makes writing controlled form components with mulitple items easier

Multiple elements

import React from "react";
import ReactDOM from "react-dom";

class Reservation extends React.Component {
 constructor(props) {
    super(props);
    this.state = {
      isGoing: true,
      numberOfGuests: 2
    };
    this.handleInputChange = this.handleInputChange.bind(this);
    this.handleSubmit = this.handleSubmit.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
  });
 }
				

Multiple elements

  handleSubmit(event) {
    alert(`A reservation was submitted: ${this.state.isGoing ?
			"We're going" : "Not going"}
			with ${this.state.numberOfGuests} guest(s)`);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Is going:
          <input
            name="isGoing"
            type="checkbox"
            checked={this.state.isGoing}
            onChange={this.handleInputChange} />
        </label>
        <br />
			

Multiple elements

        <label>
          Number of guests:
          <select
            name="numberOfGuests"
            value={this.state.numberOfGuests}
            onChange={this.handleInputChange}>
            <option value="0">None</option>
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
            <option value="4">4</option>
          </select>
        </label>
        <br />
        <input type="submit" value="RSVP" />
      </form>
    );
  }
}
ReactDOM.render(
  <Reservation />,
  document.getElementById('root')
);
				

Example Explained

  • We have one function handleInputChange that can handle both checkboxes and select elements
  • We use the name property to set the right property for state
  • On submit we again create an alert box with the data in the form

Use an API example

  • Forms are a typical component that is used to make API calls
  • Dependent on user input, you can make an API call that gives users more information
  • Let's write a form that gets data from OpenWeatherMap to say what the temperature is for a given city

Example

import React from "react";
import ReactDOM from "react-dom";

class CityForm extends React.Component {
 constructor(props) {
  super(props);
  this.state = {city: 'berkeley', temp: 0};
  this.handleInputChange = this.handleInputChange.bind(this);
 }

 getWeather(city) {
   let that = this;
   fetch(`http://api.openweathermap.org/data/2.5/weather?q=${city}&appid=af578739923ac7f173a6054b24c606ea`)
    .then(function (response) {
       return response.json();
     }).then(function (result) {
       that.setState({"temp":result.main.temp - 273.15, "city":city})
     });
 }

 componentDidMount() {
   this.getWeather(this.state.city);
 }

			

Example

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

 render() {
 return (
  <div>
  <form>
   <label>
    City:
    <select
      name="city"
      value={this.state.city}
      onChange={this.handleInputChange}>
      <option value="berkeley">Berkeley, CA</option>
      <option value="manhattan">New York, NY</option>
      <option value="chicago">Chicago, IL</option>
    </select>
   </label>
  </form>
		

Example

   <div>The temperature (in celsius) of {this.state.city}
	 	is {this.state.temp}</div>
   </div>
  );
 }
}
ReactDOM.render(
 <CityForm />,
 document.getElementById('root')
);
			

Example Explained

  • This CityForm has a function getWeather that makes an api call to openweathermap.org to get the weather
  • It is using what's called the fetch api that enables you to chain a number of actions that occur when you get results back
  • When results come back, the state is updated and is visible on the div
  • We call the openweathermap api on select input change and well as the componentDidMount function so that we have a temperature on page load

Questions?