RockOn pt 4: Controlled Forms in React
Tags: javascript, react
Published: 2020-07-17 18:23:13

*This is the fourth post in my series about building [RockOn](https://rockonapp.com). Get the full background starting [here](https://dev.to/dianakw8591/rockon-pt-1-using-nokogiri-to-seed-my-database-2h05).*

In my previous posts I discussed all the work that went into seeding my database and building a search interface - now we've arrived at the point where a user is finally entering information about a particular ascent. Let's say our user has just climbed the iconic Exum Ridge in Grand Teton National Park. Here's the form they'd see after selecting that climb:

![climbing log form](https://dev-to-uploads.s3.amazonaws.com/i/icbyeczdkyewyehehxg9.png)

Let's walk through this. The first thing the user selects is the date, which can be selected from a dropdown calendar. I used the `react-date-picker` component for this which was easy to set up and integrate, once I got the date formatting correct (I highly recommend using [moment](https://momentjs.com/) for handling dates in Javascript).

![dropdown calendar view](https://dev-to-uploads.s3.amazonaws.com/i/vx0mv8z0sggbxlo91ppw.png)

The next field, pitches, or how many rope lengths long a climb is, is autofilled from the route data. This field is modifiable by the user in the event that they have combined pitches and want to record the actual number that they climbed, or if the data from Mountain Project is incorrect. The 'Style' and 'Outcome' fields have dropdown menus to choose from, and the last four fields are just text fields for the user to type their notes into.

So for my eight fields, how many `handleChange` functions did I need for handling user input? On first glance, it might seem like each field requires its own function. But in my case, I just needed two. All the fields have the same structure, except for the date picker component which was easiest to handle with its own dedicated function. Before getting into the code, a quick review of controlled forms.

From the official [React documentation](https://reactjs.org/docs/uncontrolled-components.html#:~:text=In%20a%20controlled%20component%2C%20form,form%20values%20from%20the%20DOM.):

>In a controlled component, form data is handled by a React component. The alternative is uncontrolled components, where form data is handled by the DOM itself.

What this means in practice is that when the user enters input in a controlled form, React state is updated. That state then controls the value of the form. In this way a single source of truth is retained, managed by state.

To actually code this, there are three parts to consider: state, the `handleChange` function, and the form field itself. To begin, I set the state of all the fields. In my class based component, that looks like this:
```react
  state = {
    error: false,
    fields: {
      pitches: this.props.climb.pitches || 0,
      start_date: new Date(),
      style: '',
      outcome: '',
      partners: '',
      beta: '',
      rack: '',
      notes: '',
    }
  };
```
where the `error` state is used to display error messages on a failed form submittal. 

My form fields all look essentially the same, where `name` denotes what the field is for and corresponds to the correct key in state, `value` is controlled by the corresponding state, and all of the `onChange` functions are the same and pass `event` as the argument. Here's the form field for partners (using Bootstrap React components):
```react
<Form.Control
    type="text"
    name="partners"
    onChange={event => this.handleChange(event)}
    value={this.state.fields.partners}
/>
```
`onChange` is written as a callback here, but `onChange={this.handleChange}` would do the same thing.

By formatting all of my fields with the same structure, I was able to use one `handleChange` function for (almost all) of my fields. The `event` itself contained all the information I needed to update the appropriate state!
```react
  handleChange = e => {
    const newFields = { ...this.state.fields, [e.target.name]: e.target.value };
    this.setState({ fields: newFields });
  };
```
So in the case of updating the partners field, the spread operator will first populate my `newFields` object with all of the existing key/value pairs in `state.fields`. Because of the order, whatever `[e.target.name]` evaluates to will override any key that comes before it in the object. In the case of partners, `[e.target.field]` evaluates to just that, partners, with whatever the user has typed (`e.target.value`) as the new value, and resets whatever was previously the value of `partners`. 

It's important to remember that in Javascript, object keys are always strings. To save some typing, Javascript automatically converts keys to strings, like in my original state object. I haven't bothered to put quotes around the keys, (`fields:` rather than `"fields":`) but Javascript is reading the keys as strings anyways. In the case of `e.target.value`, I want the key to be whatever that **evaluates** to, not the string `"e.target.value"`. The brackets tell Javascript to do just that. 

The only field that didn't follow this format was the date. That component supplied the new date as the argument to its `onChange` function rather than the event, so I chose to write a separate `handleDateChange` function and handle that case.

By having all of my form fields follow the same structure, I saved myself a lot of repeat code. Another place where I implemented the same tactic was building my dropdown menus for the 'Style' and 'Outcome' fields. All my options needed to be wrapped in `<option>` tags, and I also had multiple 'Outcome' lists depending on the type of climb that was being recorded. To save some typing, I simply created arrays of 'Style' and 'Outcome' choices, and then mapped those values into a new array of `<option>` tags that I used in the form itself:
```react
  createOptions = (array) => {
    return array.map(e => <option value={e} key={e}>{e}</option>)
  }
```
By setting the value of each option this way, I was able to use the same `handleChange` function described above.

On form submittal, all of the information I need to send to my back end is already saved in state and formatted correctly. Once I post successfully, I clear most of my form values using state, except for the date to allow easy entry of another climb on the same day!

Now that a user has some data entered, what actually happens with that data? Next week I'll dive into the graphs and logbook entries I use to give users an easy way to view and manipulate their own data.

Thanks for reading!