Submitting a form in React

In order to learn React I’ve been building some simple projects. For one of those projects I wanted to create a form, with a text box and submit button. I was surprised that it wasn’t a trivial task (probably due to my own lack of knowledge). In this blog post I’ll explain how to create a submit form in React. Before diving into this blog post you may find it helpful to read through Thinking in react. It’s a great resource and really helps you to understand how React applications should be written.

Here is all of the React code for my simple application. The purpose of the application is to allow the user to create new Github web hooks. At this stage the application doesn’t do anything clever, it simply allows the user to input a new web hook name, which gets added to the list of web hooks. Don’t worry about understanding all of the code at once, I’ll explain each part in more detail.

import React, { Component } from 'react';
import './App.css';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      webhooks: [],
      value: '',
    };

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

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

  handleSubmit(event) {
    event.preventDefault();
    this.setState({webhooks: this.state.webhooks.concat(this.state.value)});
  }

  render() {
    return (
      <div className="App">
        <Title />
        <Introduction />
        <Description />

        <WebhookTable webhooks={this.state.webhooks}/>
        <InputWebhook handleChange={this.handleChange} handleSubmit={this.handleSubmit} value={this.state.value}/>
      </div>
    );
  }
}

class Title extends Component {
  render() {
    return (
      <h1>JIRA Webhooks</h1>
    )
  }
}

class Introduction extends Component {
  render() {
    return (
      <p className="description">
        Welcome to the JIRA Webhooks app. This app will allow you to create, update and delete webhooks.
      </p>
    )
  }
}

class Description extends Component {
  render() {
    return (
      <p className="description">
        To add your first webhook, scroll down and click "Add Webhook".
      </p>
    )
  }
}

class WebhookTable extends Component {
  render() {
    return (
      <ul className="webhooks_content">
        {this.props.webhooks.map(function(webhook) {
          return (
            <WebhookRow row={webhook}/>
          );
        })}
      </ul>
    )
  }
}

class WebhookRow extends Component {
  render() {
    return (
      <li>{this.props.row}</li>
    )
  }
}

class InputWebhook extends Component {
  render() {
    return (
      <form className="webhooks_content" onSubmit={this.props.handleSubmit}>
        <label>
          Add a new webhook: <input type="text" value={this.props.value} onChange={this.props.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

export default App;

In React, data should always flow down the component hierarchy, which means its important to make the right choice in terms of which components own data. React components can receive data, in the form of props, but props should not be modified. With that in mind, lets take a look at the InputWebhook component:

class InputWebhook extends Component {
  render() {
    return (
      <form className="webhooks_content" onSubmit={this.props.handleSubmit}>
        <label>
          Add a new webhook: <input type="text" value={this.props.value} onChange={this.props.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

To submit a form you need to have an onSubmit handler. In this case I’ve defined the onSubmit handler inside the App component and then passed that down into the InputWebhook component, where the form lives. This was not intuitive to me at first, I thought it should live inside the InputWebhook component because that is where the form and submit button live. The problem with this is that the state needs to live inside the App component so that it can be shared with the rest of your components. If you define your onSubmit handler inside InputWebhook, then you can’t alert the rest of the components because in React, state can’t travel up, it should only travel down. This is why you need to pass the onSubmit event handler down into InputWebhook.

The form has an onSubmit action that triggers the handleSubmit method, that was passed into the component. The form has an input box whose value is tied to this.props.value, this allows the value to be used by the App component. Lastly, the text box has an onChange action that triggers the handleChange method, that is passed into the component. This method is responsible for updating the App components state with the value in the text box. Again, this is just to allow the App component to have access to this data.

Now lets take a look at the App component.

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      webhooks: [],
      value: '',
    };

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

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

  handleSubmit(event) {
    event.preventDefault();
    this.setState({webhooks: this.state.webhooks.concat(this.state.value)});
  }

  render() {
    return (
      <div className="App">
        <Title />
        <Introduction />
        <Description />

        <WebhookTable webhooks={this.state.webhooks}/>
        <InputWebhook handleChange={this.handleChange} handleSubmit={this.handleSubmit} value={this.state.value}/>
      </div>
    );
  }
}

The App component defines the initial state. webhooks is initialized with an empty array, which will be used to populate with web hooks. value is initialised as an empty string which will be used to hold the current value of the text box. Next, I need to bind this, which is necessary to make this work in the callback. After that I define two functions for handling the text box changes and for handling submitting the form. The handleChange function is pretty simple, it used React’s setState method to update the value. HandleSubmit gets the current value of state.value and adds it to the array of webhooks. The rest of App is responsible for rendering the other React components that make up the page and passing data to the components that require it.

I hope this helps you to understand how to create submit button forms in React.