Learn React - Events and Forms

January 27, 2019

Hey folks. In our last post, we talked about components, state and props. In this one, we will talk about events and forms. These two are vital subjects for any web app, and despite not being really difficult in React, they present certain particularities.

Let’s start with the same boilerplate that we have been using in all of our previous posts about React. You can find it here: https://github.com/felipegalvao/webpack-4-react-boilerplate

Let’s go!

Events

First of all, what are events? Events are used to notify your code that something interesting is happening. This interesting thing can be triggered by the user or by the page itself. For example, a user clicks on a button, or a page finished loading, or the value of a text input changes.

With that out of the way, we can continue. In React, events are kinda similar to basic HTML, but with some differences. Let’s see the example for the onClick event. While in HTML we would do:

<button href="#" onclick="alert('row was added!');">
  Add row
</button>

In React, we have our code inside of curly braces, and it would look like that (with the code for the component):

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

import './styles/main.scss';

class Index extends Component {
  render() {
    return <div>
      <button onClick={ () => alert('row was added!') }>add row</button>
    </div>;
  }
};

ReactDOM.render(<Index />, document.getElementById("index"));

In React, to run the Javascript code directly within the event, we have to use an arrow function and have the code in this way, so that the code won’t run the component is rendered. It is also possible to call a predefined function. In HTML, we would do this:

<button onclick="handleClick()">
  add row
</button>

In React, we will put the function inside the curly braces. As we already saw in the post about state and props, for the this keyword to work, we need to bind it in the component’s constructor:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

import './styles/main.scss';

class Index extends Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    alert('row was added!');
  }

  render() {
    return <div>
      <button onClick={ this.handleClick }>add row</button>
    </div>;
  }
};

ReactDOM.render(<Index />, document.getElementById("index"));

If this bothers you, there are two other ways to make it work, as explained on the same post. I, particularly, prefere arrow functions to handle this type of problem.

Well, after writing the code above, you can click on the button and you will get the browser alert. onClick is the most common event for buttons.

Another event that is really common is the onChange event, mostly used with the input element. The onChange event is triggered every time the value of the input changes. Let’s see that behavior using the code below:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

import './styles/main.scss';

class Index extends Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
    this.handleTextChange = this.handleTextChange.bind(this);
    this.state = {currentText: ''}
  }

  handleClick() {
    alert('row was added!');
  }

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

  render() {
    return <div>
      <button onClick={ this.handleClick }>add row</button>
      <input
        type="text"
        placeholder="enter your name here"
        onChange={ this.handleTextChange }
      />
      { this.state.currentText }
    </div>;
  }
};

ReactDOM.render(<Index />, document.getElementById("index"));

Start typing on the text input and see how the paragraph will change accordingly. Note that in this case, we have to set a initial state with an empty string. If we don’t do this, the paragraph will break when trying to render, as the part of the state that we will try to reference will not be defined.

Forms - controlled components

Having learned about events, let’s now talk about forms. Forms, as we already talked about, are a huge part of a great number of web apps, and because of that, it’s important to understand how they work on React.

In React, the most common way to work with forms is through the concept called controlled components. For this, we make so that the state is the single source of truth for the values in the form, and we use the events on the elements of the form to keep updating the values on the state. And then you call the function to submit the form on the onSubmit event for the form. For a simple form, with a text input, it would be like this:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

import './styles/main.scss';

class Index extends Component {
  constructor(props) {
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.handleTextChange = this.handleTextChange.bind(this);
    this.state = {name: ''}
  }

  handleSubmit(event) {
    alert('Your name was sent to our API, ' + this.state.name);
    event.preventDefault();
  }

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

  render() {
    return <div>
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input
            type="text"
            placeholder="enter your name here"
            onChange={ this.handleTextChange }
            value={ this.state.currentText }
          />
        </label>
        <input type="submit" value="Send" />
      </form>
    </div>;
  }
};

ReactDOM.render(<Index />, document.getElementById("index"));

Type your name and then click on the Send button, and you will see the alert with the name you typed in the input. Let’s add a select element:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

import './styles/main.scss';

class Index extends Component {
  constructor(props) {
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.handleTextChange = this.handleTextChange.bind(this);
    this.handleColorSelect = this.handleColorSelect.bind(this);
    this.state = {name: '', favoriteColor: 'blue'}
  }

  handleSubmit(event) {
    alert(
      `Your name is ${this.state.name} and your favorite color is ${this.state.favoriteColor}`
    );
    event.preventDefault();
  }

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

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

  render() {
    return <div>
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input
            type="text"
            placeholder="enter your name here"
            onChange={ this.handleTextChange }
            value={ this.state.currentText }
          />
        </label>
        <select value={this.state.favoriteColor} onChange={this.handleColorSelect}>
          <option value="blue">Blue</option>
          <option value="red">Red</option>
          <option value="green">Green</option>
          <option value="black">Black</option>
        </select>
        <input type="submit" value="Send" />
      </form>
    </div>;
  }
};

ReactDOM.render(<Index />, document.getElementById("index"));

For the select element, you create each option inside of the element with its own value, and then you pass the event to the function to be called when the select element value is changed. Go on and test it, selecting your favorite color and clicking on the button to submit the form.

To finish this post, let’s see how radio buttons work in React. It’s pretty similar to the select element. I’ll add some div to better organize the code and the layout, and then I’ll add the radio buttons:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

import './styles/main.scss';

class Index extends Component {
  constructor(props) {
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.handleTextChange = this.handleTextChange.bind(this);
    this.handleColorSelect = this.handleColorSelect.bind(this);
    this.handleAnimalSelect = this.handleAnimalSelect.bind(this);
    this.state = {name: '', favoriteColor: 'blue', favoriteAnimal: ''}
  }

  handleSubmit(event) {
    alert(
      `Your name is ${this.state.name}, your favorite color is ${this.state.favoriteColor}` +
      `and your favorite animal is ${this.state.favoriteAnimal}`
    );
    event.preventDefault();
  }

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

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

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

  render() {
    return <div>
      Insert your name, your favorite color and your favorite animal.
      <form onSubmit={this.handleSubmit}>
        <div>
          <label>
            Name:
            <input
              type="text"
              placeholder="enter your name here"
              onChange={ this.handleTextChange }
              value={ this.state.currentText }
            />
          </label>
        </div>
        <div>
          <select value={this.state.favoriteColor} onChange={this.handleColorSelect}>
            <option value="blue">Blue</option>
            <option value="red">Red</option>
            <option value="green">Green</option>
            <option value="black">Black</option>
          </select>
        </div>
        <div>
          <label>
            <input
              type="radio"
              name="react-tips"
              value="dog"
              checked={this.state.favoriteAnimal === "dog"}
              onChange={this.handleAnimalSelect}
            />
            Dog
          </label>
        </div>
        <div>
          <label>
            <input
              type="radio"
              name="react-tips"
              value="cat"
              checked={this.state.favoriteAnimal === "cat"}
              onChange={this.handleAnimalSelect}
            />
            Cat
          </label>
        </div>
        <div>
          <input type="submit" value="Send" />
        </div>
      </form>
    </div>
  }
};

ReactDOM.render(<Index />, document.getElementById("index"));

Note that, for the radio buttons, we define if it’s checked or not by comparing it’s value with the value that is currently in the state.

And with this, we were able to learn how to work with forms in React, and also how to use the most common form elements.

In “real life” examples, the submit of a form would probably make a request to an API with its values, possibly using Axios, fetch or any other way you prefer. But this is a subject for another post.

Cheers and I hope this is helpful.