Practical Tips for Handling Forms in React
Tags: react, javascript
Published: 2020-05-03 22:02:29

---
title: "Practical Tips for Handling Forms in React"
published: true
description: "Some tips to make working with Form Inputs in React more easier"
categories: [react, javascript]
tags: react, javascript
author: John Alcher
cover_image: https://cdn.stocksnap.io/img-thumbs/960w/writing-hand_2FRRD2PUVA.jpg
canonical_url: https://johnalcher.me/2020/practical-tips-for-handlng-forms-in-react/
---

## Preface

This is a snippet from my notes as I learn ReactJS for work. If you have any suggestions on how I can improve my code samples, or if you found anything catastrophically wrong, please do not hesitate to let me know!

## Contents

1. [Introduction](#introduction)
2. [Create a Generic `onChange` Handler](#create-a-generic-onchange-handler)
3. [Reset a Form Through an `initialState`](#reset-a-form-through-an-initialstate)
4. [Move State Closer to Forms](#move-state-closer-to-forms)
5. [Conclusion](#conclusion)

## Introduction

So you've learned about what [React](https://reactjs.org/) is and why is it all over the place these days. You've learned what [components and props](https://reactjs.org/docs/components-and-props.html) are, as well as how to manage their [state and lifecycle](https://reactjs.org/docs/state-and-lifecycle.html). You are also now familiar with the concept of [controlled components](https://reactjs.org/docs/forms.html#controlled-components) (i.e. how to manage state through form inputs). In this article, we'll take a look at a few techniques that we can utilize in order to make working with Form Inputs in React more easier.

> Note: Examples in this article heavily use ES6 features. If you are not familiar with ES6, [Tania Rascia's overview](https://www.taniarascia.com/es6-syntax-and-feature-overview/) is a good place to start.

## Create a Generic `onChange` Handler

In order to achieve parity on a `state` and `<input/>` value (also called **two-way data binding**), we need to set an `<input/>`'s value to its corresponding `state` and also bind an `onChange` handler that computes the new `state` value when the `<input/>` has been changed. Let's take a look at an example from the ReactJS website (refactored for brevity):

```jsx
class RegistrationForm extends React.Component {
  state = { name: '' }

  handleChange = event => this.setState({name: event.target.value})

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

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

What this example does is that when the `<input/>`'s value changes, the `state.name` property is also updated. But the state being updated (`name`) is hardcoded, which prevents it from being reusable when there are multiple inputs. A solution that I commonly see is to create a handler for each input, which would like this:

```jsx
state = { name: '', password: '' }

handleNameChange = event => this.setState({name: event.target.value})

handlePasswordChange = event => this.setState({password: event.target.value})

render() {
    return (
        <form onSubmit={this.handleSubmit}>
            <label>
                Name:
                <input type="text"
                    value={this.state.name}
                    onChange={this.handleNameChange} />
            </label>
            
            <label>
                Password:
                <input type="password"
                    value={this.state.password}
                    onChange={this.handlePasswordChange} />
            </label>

            <input type="submit"
                value="Submit" />
        </form>
    );
}
```

If we would be working with one or two `<input/>`s, this approach would work just fine. But one can imagine when requirements down the road dictates that we need to add more field to this form, then a 1:1 input to handler ratio would quickly become unmantainable. This is where a **Generic Handler** comes in. 

As the name implies, a Generic Handler catches all input events and updates their corresponding state. The key that will be used for the state lookup will be inferred from the `name` attribute of an `<input/>`. This is what it looks like:

```jsx
handleChange = event => {
    const {name, value} = event.target;

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

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

            <input type="submit"
                value="Submit" />
        </form>
    );
}
```

Now both `<input/>`s only use one handler to update their corresponding state. But what if we need to apply custom logic to specific `<input/>`s before updating the state? An example would be to validate if an `<input/>`'s value is valid, or to apply formatting to specific value. We can do this by checking the `name` of the `<input/>` and conditionally applying the desired logic:

```jsx
state = {
    name: '',
    password: '',
    age: null,
}

handleChange = event => {
    let {name, value} = event.target;

    // Custom validation and transformation for the `age` input
    if (name === 'age') {
        value = parseInt(value);
        if (value < 18) {
            alert('Minors are not allowed');
            return;
        }
    }

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

handleSubmit = event => {
    event.preventDefault();
    console.log(JSON.stringify(this.state)); // Ready for processing
}

render() {
    return (
        <form onSubmit={this.handleSubmit}>
            <label>
                Name:
                <input type="text"
                    value={this.state.name}
                    onChange={this.handleChange} />
            </label>
            
            <label>
                Password:
                <input type="password"
                    value={this.state.password}
                    onChange={this.handleChange} />
            </label>
            
            <label>
                Age:
                <input type="number"
                    value={this.state.age}
                    onChange={this.handleChange} />
            </label>

            <input type="submit"
                value="Submit" />
        </form>
    );
}
```

If the `handleChange` method becomes too bloated down the line because of the multiple branches, you might want to consider factoring the complex `<input/>`s onto their own component and manage the logic there.

## Reset a Form through an `initialState`

As you might have already experienced, a common process when using an HTML form that *creates something* is:

1. Enter data into the form fields.
2. Submit the form.
3. Wait for the data to be processed (by an HTTP request to a server, for example).
4. Enter data again onto a cleared form.

We already have steps 1 to 3 (if we count the `console.log` call as step #3) implemented in the previous example. How could we implement step #4? A perfectly fine (though somewhat naive) solution is to call `setState` and pass what the original `state` object might contain:

```jsx
state = {
    name: '',
    password: '',
    age: null,
}

handleSubmit = event => {
    event.preventDefault();
    console.log(JSON.stringify(this.state)); // Ready for processing

    // Reset the state
    this.setState({
        name: '',
        password: '',
        age: null,
    });
}
```

Copy and pasting, more often than not, is a good indicator that a better solution is available. What if we add more fields in the future? What if we only want to reset some parts of the form? These could be easily solved by creating an `initialState` member on your class:

```jsx
initialState = {
    name: '',
    password: '',
    age: null,
}

state = { ...this.initialState }

handleSubmit = event => {
    event.preventDefault();
    console.log(JSON.stringify(this.state)); // Ready for processing

    // Reset the state
    this.setState({ ...this.initialState });
}
```

Want to persist the `name` when the form is cleared? Simply move it from the `initialState` to the `state` and it won't get overwritten upon submission:

```jsx
initialState = {
    password: '',
    age: null,
}

state = {
    name: '',
    ...this.initialState
}

handleSubmit = event => {
    event.preventDefault();
    console.log(JSON.stringify(this.state)); // Ready for processing

    // Reset the state except for `name`
    this.setState({ ...this.initialState });
}
```

### Move State Closer to Forms

With React, it is tempting to move all state as high up the component tree as possible and just pass down props and handlers when necessary. 
Functional components are easier to reason with after all. But this could lead to bloated state if we shoehorn everything on the top-level component. 

To demonstrate, let's say that the `<RegistrationForm/>` component in the previous example is under an `<App/>` component in the component tree. `<App/>` keeps an array of users in its state and we would like to push the newly registered user from the `<RegistrationForm/>` component. Our first instict might be to move state up to the `<App/>` component and make `<RegistrationForm/>` a functional one:

```jsx
class App extends React.Component {
    state = {
        users: [],
        newUser: {
            name: '',
            password: '',
            age: null,
        },
    }

    handleChange = e => {
        let {name, value} = event.target;

        // Custom validation and transformation for the `age` input
        if (name === 'age') {
            value = parseInt(value);
            if (value < 18) {
                alert('Minors are not allowed');
                return;
            }
        }

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

    handleSubmit = e => {
        e.preventDefault();

        const users = this.state.users.slice();
        const {name, password, age} = this.state.newUser;
        users.push({name, password, age});

        this.setState({users});
    }

    render() {
        return <RegistrationForm newUser={this.state.newUser}
            handleChange={this.handleChange}
            handleSubmit={this.handleSubmit}/>
    }
}

const RegistrationForm = ({newUser, handleChange, handleSubmit}) => (
    <form onSubmit={handleSubmit}>
        <label>
            Name:
            <input type="text"
                value={newUser.name}
                onChange={handleChange} />
        </label>
        
        <label>
            Password:
            <input type="password"
                value={newUser.password}
                onChange={handleChange} />
        </label>
        
        <label>
            Age:
            <input type="number"
                value={newUser.age}
                onChange={handleChange} />
        </label>

        <input type="submit"
            value="Submit" />
    </form>
)
```

This solution works, and nothing is inherently wrong with it. But let's take a step back and look at it with fresh eyes: *does the `<App/>` component really care about the `newUser` state?* Opinions might vary, but heres mine: I think that unless `<App/>` manages other components that might need to access it, the `newUser` data should be managed solely by who it's concerned with -- `<RegistrationForm/>`. The `<App/>` component doesn't necessarily care about the low-level details, it just wants **a way to add a new user**.

Let's do just that!

```jsx
class App extends React.Component {
    state = { users: [] }

    addUser = user => {
        const users = this.state.users.slice();
        users.push(user);

        this.setState({ users });
    }

    render() {
        return <RegistrationForm addUser={this.addUser}/>
    }
}

class RegistrationForm extends React.Component {
    state = {
        name: '',
        password: '',
        age: null,
    }

    handleChange = e => {
        let {name, value} = event.target;

        // Custom validation and transformation for the `age` input
        if (name === 'age') {
            value = parseInt(value);
            if (value < 18) {
                alert('Minors are not allowed');
                return;
            }
        }

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

    handleSubmit = e => {
        e.preventDefault();
        this.props.addUser(this.state);
    }

    render() {
        const {name, password, age} = this.state;

        return (
            <form onSubmit={this.handleSubmit}>
                <label>
                    Name:
                    <input type="text"
                        value={name}
                        onChange={this.handleChange} />
                </label>
                
                <label>
                    Password:
                    <input type="password"
                        value={password}
                        onChange={this.handleChange} />
                </label>
                
                <label>
                    Age:
                    <input type="number"
                        value={age}
                        onChange={this.handleChange} />
                </label>

                <input type="submit"
                    value="Submit" />
            </form>
        );
    }
}
```

See the difference? Now, `<App/>` itself doesn't know how the `newUser` object is being built. It doesn't have handlers that work with DOM events, which makes sense since *it doesn't render any form inputs itself*. `<RegistrationForm/>`, on the other hand, returns HTML `<input/>`s directly, and it only makes sense that it handles input events on its own.

## Conclusion

Things to take away from this article:

1. A generic `onChange` handler can reduce repeated handler code.
2. Inferring state from an `initialState` can be useful for resetting a component's state.
3. Think twice when moving state up the component tree.
4. Components that render HTML `<input/>`s directly should be the one with event handlers.

## Links and References

- [ReactJS Official Website](https://reactjs.org/)
- [ES6 Syntax and Feature Overview](https://www.taniarascia.com/es6-syntax-and-feature-overview/)