---
status: "published"
title: "Learning Form Management in React"
date: "2018-10-08"
description: "By building our own reusable component from first principles."
image: "https://github.com/user-attachments/assets/3ad59f4f-05d1-4a81-9b8e-0b8bd14b0efc"
imageAlt: "Photo by rawpixel on Unsplash"
author: "Vlad Sabev"
authorUrl: "https://github.com/vdsabev"
authorAvatar: "https://github.com/vdsabev.png"
tags: ["forms","javascript","react","software development"]
navigationIndex: 0
---

Photo by [rawpixel](https://unsplash.com/@rawpixel?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=medium&utm_medium=referral)

Learning often comes from deliberate practice and repetition—making the same mistakes, writing the same code, solving the same problems over and over again.

For example, how many times have you had to:

- Render a form with some inputs
- Collect data from these inputs as the user enters values
- Process the collected data on submit

Me personally — multiple times, in every project.

## 🤔 The Simple Solution

Of course there already *are* libraries for managing forms in React. A couple of the most popular are:

- [Jared Palmer](https://medium.com/u/fb7a3c353cc1)'s [Formik](https://github.com/jaredpalmer/formik), which uses the same principles described in this post
- [redux-form](https://github.com/erikras/redux-form), which manages form state via Redux (well, duh)

**If you're in a real hurry to put some forms in your project I encourage you to stop reading right now and use one of those libraries — they're full-featured, well-tested, and have a significant community around them.**

Otherwise, I've found that to understand a tool in more depth it really helps to try and build your own and *then* switch to a library if you really need to.

In fact, I only started appreciating the benefits and trade-offs of Redux when I went through the process of writing [a state management library](https://github.com/vdsabev/overstate) myself.

We'll be doing something similar here — by writing a reusable `<Form>` component from first principles, we can learn a thing or two about *why* libraries are built the way they are, as well as *how* to use them more effectively.

Let's go.

## 🍰 The Delicious Demo

TL;DR — here's what we'll be building:

<iframe height="450" style="width:100%;" scrolling="no" title="" src="https://codepen.io/vdsabev/embed/LgRQKd?default-tab=html%2Cresult" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"></iframe>

Now I'm sure you could find your way around the code just fine — still, as part of the learning experience, I'll go over the process in more detail.

## 1️⃣ The Boring Beginning

To figure out what problems we're trying to solve, let's first write a standard React form from scratch:

```javascript
class App extends React.Component {
  state = {};

  submit = (e) => {
    e.preventDefault(); // Prevent submitting form to the server
    window.alert(`Hey, ${this.state.name}! You ordered some ${this.state.food}!${this.state.isDessert ? ' A lovely dessert!' : ''}`);
  };

  setName = (e) => this.setState({ name: e.target.value });
  setFood = (e) => this.setState({ food: e.target.value });
  setIsDessert = (e) => this.setState({ isDessert: e.target.checked });

  render() {
    return (
      <form onSubmit={this.submit}>
        <label>
          What's your name?
          <input type="text" onChange={this.setName} required autoFocus />
        </label>

        <label>
          What's your favorite food?
          <input type="text" onChange={this.setFood} required />
        </label>

        <label>
          <input type="checkbox" onChange={this.setIsDessert} />
          It's a dessert
        </label>

        <button type="submit">Order</button>
      </form>
    );
  }
}
```

This does exactly the same thing as the demo from the previous section — it renders a form with:

- Two text inputs
- One checkbox
- And a submit button.

When the form is submitted it shows an alert dialog that "orders" your favorite food.

## 2️⃣ The Refactoring Round

One painfully dull thing was that we wrote three methods for collecting values from each input. So if we had 300 inputs would we then have to write 300 methods?

Let's change that by using a more generic method instead:

```javascript
class App extends React.Component {
  ...

  setValue = (e) => {
    const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
    this.setState({ [e.target.name]: value });
  };

  render() {
    return (
      <form onSubmit={this.submit}>
        <label>
          What's your name?
          <input type="text" name="name" onChange={this.setValue} required autoFocus />
        </label>

        <label>
          What's your favorite food?
          <input type="text" name="food" onChange={this.setValue} required />
        </label>

        <label>
          <input type="checkbox" name="isDessert" onChange={this.setValue} />
          It's a dessert
        </label>

        <button type="submit">Order</button>
      </form>
    );
  }
}
```

The main things here are that we:

- Added a `name` attribute to inputs
- And handled checkboxes a bit differently from text inputs by using the target's `checked` attribute instead of `value`.

Now, whether we have 3 or 300 components in there, we only need to set a `name` attribute and that one `onChange` handler.

## 3️⃣ The Rad Rendering

To make this component's behavior reusable across our application, we need to be able to provide *different* inputs as its children, as well as attach `onChange` handlers from the parent component to said children.

And how can we both:

- Provide children to a parent component
- *And* allow children to access functions from their parent?

Why, by using the rad [render props](https://reactjs.org/docs/render-props.html) pattern of course!

```javascript
class Form extends React.Component {
  state = {};

  submit = (e) => {
    e.preventDefault();
    this.props.onSubmit(this.state);
  };

  setValue = (e) => {
    const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
    this.setState({ [e.target.name]: value });
  };

  render({ children, onSubmit, ...props } = this.props) {
    return (
      <form onSubmit={this.submit} {...props}>
        {children({ setValue: this.setValue })}
      </form>
    );
  }
}
```

The trick here is that the `children` props of our component is actually a function — one that we call with an object exposing some API from the `<Form>` component to its children — in this case just the `setValue` function.

We also automatically prevent the form's default behavior of submitting to the server and call the provided `onSubmit` prop with the collected form values instead.

## 4️⃣ The Complete Code

Our `<Form>` component is now reusable across our entire application, and even across many different React applications if you think about it!

So let's use it in our `<App>` component:

```javascript
class App extends React.Component {
  submit = (values) => {
    window.alert(`Hey, ${values.name}! You ordered some ${values.food}!${values.isDessert ? ' A lovely dessert!' : ''}`);
  }

  render() {
    return (
      <Form onSubmit={this.submit}>
        {({ setValue }) => (
          <fieldset>
            <label>
              What's your name?
              <input type="text" name="name" onChange={setValue} required autoFocus />
            </label>

            <label>
              What's your favorite food?
              <input type="text" name="food" onChange={setValue} required />
            </label>

            <label>
              <input type="checkbox" name="isDessert" onChange={setValue} />
              It's a dessert
            </label>

            <button type="submit">Order</button>
          </fieldset>
        )}
      </Form>
    );
  }
}
```

We wrapped our inputs with the `<Form>` component and converted the children into a *function returning children* which exposes the `setValue` method from the `<Form>` component.

Notice that to avoid listing children as an array we wrapped them in an extra element, more specifically — a fieldset. One cool thing about the fieldset element is that disabling it also automatically disables all of its child inputs too!

## 💯 Bonus

We now have a very basic `<Form>` component we can use in our React application. As a thought exercise, consider some additional ways we could enhance that component:

- Allow setting the initial values and make child inputs [fully controlled](https://reactjs.org/docs/forms.html#controlled-components)
- [Define children as a function in `propTypes`](https://reactjs.org/docs/render-props.html#using-props-other-than-render) in order to have a more robust `<Form>` component that throws an error when `children` isn't a function
- If you want to make `<Form>` a `React.PureComponent` to improve performance, you might also want to look into [the implications of using render props with pure components](https://reactjs.org/docs/render-props.html#be-careful-when-using-render-props-with-reactpurecomponent)
- If the form is more complex, consider using dot notation in the input's `name` attribute to handle deeply nested data — for example the component `<input type="text" name="name.first" value="Albert" />` would return the object `{ name: { first: 'Albert' } }`
- Incorporate data validation and error messages in your form by utilizing the `e.target.validationMessage` property and other [HTML5 Form Validation](https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/Form_validation) features
- Try using [JSON schema](https://json-schema.org/understanding-json-schema) to both generate form inputs *and* validate them — this also allows you to share data validation rules with other environments, for example a backend server.

## 🏁 Conclusion

Like I said at the beginning, learning often comes through deliberate practice and repetition. And after going through the process of building a simple `<Form>` component myself, I think I understand the trade-offs well enough to finally switch to a library like [Formik](https://github.com/jaredpalmer/formik).

Hope this leaves you too with plenty of enthusiasm for forms — have fun building!

