JavaScript - - By Azat Mardan

React Quickly: How to Work with Forms in React

How to Work with Forms in React is an excerpt from React Quickly, a hands-on book by Azat Mardan for anyone who wants to learn React.js fast.

This article covers how to capture text input and input via other form elements like input, textarea, and option. Working with them is paramount to web development, because they allow our applications to receive data (e.g. text) and actions (e.g. clicks) from users.

The source code for the examples in this article is in the ch07 folder of the GitHub repository azat-co/react-quickly. Some demos can be found at http://reactquickly.co/demos.

If you enjoy this post, you might also like to watch our course Build React Forms with Redux.

Recommended Way of Working with Forms in React

In regular HTML, when we work with an input element, the page’s DOM maintains that element’s value in its DOM node. It’s possible to access the value via methods like document.getElementById('email').value, or by using jQuery methods. The DOM is our storage.

In React, when working with forms or any other user input fields, such as standalone text fields or buttons, developers have an interesting problem to solve. From React’s documentation:

React components must represent the state of the view at any point in time and not only at initialization time.

React is all about keeping things simple by using declarative styles to describe UIs. React describes the UI, its end stage, and how it should look.

Can you spot a conflict? In the traditional HTML form elements, the state of the elements will change with the user input. React uses a declarative approach to describe the UI. The input needs to be dynamic to reflect the state properly.

If developers opt not to maintain the component state (in JavaScript), or not to sync it with the view, then it adds problems: there might be a situation when internal state and view are different. React won’t know about changed state. This can lead to all sorts of trouble, and mitigates the simple philosophy of React. The best practice is to keep React’s render() as close to the real DOM as possible, and that includes the data in the form elements.

Consider this example of a text input field. React must include the new value in its render() for that component. Consequently, we need to set the value for our element to new value using value. If we implement an <input> field as we always did it in HTML, React will keep the render() in sync with the real DOM. React won’t allow users to change the value. Try it yourself. It drives me nuts, but it’s the appropriate behavior for React!

render() {
  return <input type="text" name="title" value="Mr." />
}

The code above represents the view at any state, and the value will always be “Mr.”. With input fields, they must change in response to the user keystrokes. Given these points, let’s make the value dynamic.

This is a better implementation, because it’ll be updated from the state:

render() {
  return <input type="text" name="title" value={this.state.title} />
}

What is the value of state? React can’t know about users typing in the form elements. Developers need to implement an event handler to capture changes with onChange.

handleChange(event) {
  this.setState({title: event.target.value})
}
render() {
  return <input type="text" name="title" value={this.state.title} onChange={this.handleChange.bind(this)}/>
}

Given these points, the best practice is for developers to implement the following things to sync the internal state with the view (Figure 1):

  1. define elements in render() using values from state
  2. capture changes of a form element using onChange() as they happen
  3. update the internal state in event handler
  4. save new values in state and then update the view with a new render()
Forms in React, Figure 1: Capturing changes in input and applying to state

Figure 1: Capturing changes in input and applying to state

It might seem like a lot of work at first glance, but I hope that by using React more, you’ll appreciate this approach. It’s called a one-way binding, because state only changes views. There’s no trip back, only a one-way trip from state to view. With one-way binding, a library won’t update state (or model) automatically. One of the main benefits of one-way binding is that it removes complexity when working with large apps where many views can implicitly update many states (data models) and vice versa—Figure 2.

Simple doesn’t always mean less code. Sometimes, like in this case, developers must write extra code to manually set the data from event handlers to the state (which is rendered to view), but this approach tends to be superior when it comes to complex user interfaces and single-page applications with a myriad of views and states. To put it concisely: simple isn’t always easy.

Forms in React, Figure 2: One-way vs two-way binding

Figure 2: One-way vs two-way binding

Conversely, a two-way binding allows views to change states automatically without developers explicitly implementing it. The two-way binding is how Angular 1 works. Interestingly, Angular 2 borrowed the concept of one-way binding from React and made it the default (you can still have two-way binding explicitly).

Recommended Courses

Todd Motto
The ultimate resource to learn Angular and its ecosystem. Use coupon code 'SITEPOINT' at checkout to get 25% off.

For this reason, we’ll cover the recommended approach of working with forms first. It’s called controlled components and it ensures that the internal component state is always in sync with the view. The alternative approach is uncontrolled component.

So far, we’ve learned the best practice for working with input fields in React, which is to capture the change and apply it to state as depicted in Figure 1 (input to changed view). Next, let’s look at how we define a form and its elements.

Defining the Form and Its Events in React

We’ll start with the <form> element. Typically, we don’t want our input elements hanging randomly in the DOM. This can turn bad if we have many functionally different sets of inputs. Instead, we wrap input elements that share a common purpose in a <form></form> element.

Having a <form> wrapper isn’t necessary. It’s totally fine to use form elements by themselves in simple user interfaces. In more complex UIs, developers might have multiple groups of elements on a single page. In this case, it’s wise to use <form> for each of group. React’s <form> is rendered at an HTML <form>, and whatever rules we have apply to React’s <form> element too. According to the HTML5 spec, developers shouldn’t nest forms (it says content is flow content, but with no <form> element descendants).

The form element itself can have events. React supports three events for forms in addition to standard React DOM events:

  • onChange: fires when there’s a change in any of the form’s input elements.
  • onInput: fires for each change in <textarea> and <input> elements values. The React team doesn’t recommend using it (see below).
  • onSubmit: fires when the form is submitted, usually by pressing enter.

onChange vs onInput

React’s onChange fires on every change, in contrast to the DOM’s change event, which might not fire on each value change, but fires on lost focus. For example, for <input type="text">, a user can be typing with no onChange and only after the user presses tab or clicks with their mouse away to another element (lost focus) will the onChange be fired in HTML (regular browser event). As mentioned earlier, in React, onChange fires on each keystroke, not only on lost focus.

On the other hand, onInput in React is a wrapper for the DOM’s onInput, which fires on each change. The React team recommends using onChange over onInput.

The bottom line is that React’s onChange works differently from onChange in HTML, in that it’s more consistent (and more like HTML’s onInput). onChange is triggered on every change and not on the loss of focus.

The recommended approach in React is to use onChange, and only use onInput when you need to access native behavior for the onInput event. The reason is that React’s onChange wrapper behavior provides consistency.

Recommended Courses

Wes Bos
A step-by-step training course to get you building real world React.js + Firebase apps and website components in a couple of afternoons. Use coupon code 'SITEPOINT' at checkout to get 25% off.

React events

In addition to the three events listed above, the <form> can have standard React events such as onKeyUp or onClick. Using form events might come in handy when we need to capture a specific event for the entire form (a group of input elements).

For example, it’s a good UX to allow users to submit the data on hitting enter (assuming you’re not in the textarea field, in which case enter should create a new line). We can listen to the form submit event by creating an event listener which triggers this.handleSubmit().

handleSubmit(event) {
  …
}
render() {
  <form onSubmit={this.handleSubmit}>
    <input type="text" name="email" />
  </form>
}

Please note: we’ll need to implement the handleSubmit function outside of render(), as we’d do with any other event. There’s no naming convention that React requires, and you can name the event handler anything you wish, as long as it’s understandable and consistent. For now, we’ll stick with the most popular convention, which is to prefix the event handler with the word handle to distinguish it from regular class methods.

As a reminder, don’t invoke a method (don’t use parentheses), and don’t use double quotes around the curly braces (right way: EVENT={this.METHOD}) when setting the event handler. I know that for some readers it’s basic JavaScript and straightforward, but you wouldn’t believe how many times I’ve seen errors related to these two misunderstandings in React code: we pass definition of the function, not its result, and we use curly braces as values of the JSX attributes.

Another way to implement the form submission on enter is by manually listening to key up event (onKeyUp) and checking for the key code (13 for enter).

handleKeyUp(event) {
  if (event.keyCode == 13) return this.sendData()
}
render() {
  return <form onKeyUp={this.handleKeyUp}>
  ...
  </form>
}

Please note that the sendData method is implemented somewhere else in the code. Also, we’ll need to bind(this) event handler in constructor().

To summarize, we can have events on the form element, not only on individual elements in the form.

That’s all for now! For more, download the free first chapter of React Quickly.


For more on React, check out our mini course Build React Forms with Redux. This course will cover the basics of forms in React and Redux, explain the value of Redux Form, and cover the basics of creating forms using Redux Form. Beyond that, you’ll also learn about validation of forms that were created with Redux Form, and management of their error messages, allowing you to build robust forms while retaining the ability to save the state of form items. To give you a taster of what’s in store, check out the free lesson below.

Loading the player…

Sponsors