React Controlled Forms
Tags: react, javascript, controlledforms, beginners
Published: 2022-10-17 23:31:03


#Intro
Nearly all web applications use forms. Everything from logging into social media, making new posts, and customizing your profile uses forms. React provides a useful tool that allows us to create controlled forms: forms whose component state (what the internal code tracks) matches the user interface state (what the user sees in the application).

###Quick Review
In React, **state** is data that is dynamic. It changes over time as users interact with the application. And it is used in controlled forms to give the values for each input.

To create controlled forms you should first be familiar with React Hooks, specifically the State Hook. React Hooks are special predefined functions within the React library. The State Hook, 'useState', allows developers to access/'hook into' React's internal state of our components. 

####useState
To use the State Hook, we first need to import it from the React library.

```javascript
import React, { useState } from "react";

function Component () {
    const [stateVar, setStateVar] = useState(""):
    
    return <h1>{stateVar}</h1>;
}
```
Once imported, we can use the State Hook by invoking the useState function. This function returns an array of two elements: a state variable and a setter function. When invoking useState, you can declare the state variable's initial value, in this case an empty string.

To update a state variable, you must invoke the setter function. After a setter function is invoked, the state is set asynchronously and upon completion, the component is rerendered. If we were to call `setStateVar("Hello World")` our program will begin to asynchronously set the state variable to `"Hello World"` and upon rerender our `h1` will read `Hello world`.

Although there is more to the State Hook, we can move onto how they are used to create controlled forms.

#Controlled Forms
Controlled forms are forms that derive the values for its inputs from state. Let's look at a simple example of a controlled form:

```javascript
import React, { useState } from "react";

function Form() {
  const [firstName, setFirstName] = useState("John");
  const [lastName, setLastName] = useState("Henry");

  return (
    <form>
      <input type="text" value={firstName} />
      <input type="text" value={lastName} />
      <button type="submit">Submit</button>
    </form>
  );
}

```
The above example is a controlled form because the values for the input fields are defined by their respective state variables. But our program as it is would not allow a user to change the values of these inputs. So how do we allow for users to actually fill out the form and change the state of our application?

Input fields can listen for many different event types; for this instance we will use `onChange`. `onChange` will trigger every time the value of the input changes. 

```javascript
<input type="text" onChange={handleFirstNameChange} value={firstName} />
```

Now everytime the user alters the input element, it will call the function `handleFirstNameChange` and pass the event object as an argument of the function. 

```javascript
function handleFirstNameChange(event) {
  setFirstName(event.target.value);
}
```

From this event argument, we can access the value currently entered in our input element: `event.target.value` and use our setter function to update our state variable. Adding a similar function for our `lastName` input and state variable would allow the user to interact with the form while changing state within the component.

###Abstractions
In our previous example, we only had two state variables. But in a world where forms often ask for passwords, addresses, emails, phone numbers, etc., creating unique state variables and invoking unique callback functions for each, while doable, can easily become repetitve. It may be preferable to have a single state object variable.

For simplicity we will build off our previous example with only the `firstName` and `lastName` state variables. But we will consolidate them into one object variable. 

```javascript
import React, { useState } from "react";

function Form() {
  //const [firstName, setFirstName] = useState("John");
  //const [lastName, setLastName] = useState("Henry");
    const [userObj, setUserObj] = useState({firstName: "John" , 
                                            lastName: "Henry"})

  return (
    <form>
      <input type="text" value={userObj.firstName} />
      <input type="text" value={userObj.lastName} />
      <button type="submit">Submit</button>
    </form>
  );
}
```
Here you can see we replaced our two state variables with one by creating the `nameObj` state variable with the default value of an object with keys `firstName` and `lastName`.

Making these input fields dynamic requires a bit more complexity than using individual callback `handleChange` functions, but it is very useful and easy once you get used to it.
```javascript
<input type="text" id = "firstName" onChange={handleUserChange} value={userObj.firstName} />
<input type="text" id = "lastName" onChange={handleUserChange} value={userObj.lastName} />
```
Like in our previous build, we have an onChange event listener that takes a callback handler. But this time we have added an id prop which will be used in our event handler to determine which field is being updated.

Unlike our previous build, we cannot simply call the event handler and directly set the state variable to the value of the current state. Since the same event handler is being used on two different inputs, we need a way to differentiate each input to know which value we intend to update. Additionally, we need to be careful not to simply use `setUserObj(event.target.value)` as this would mean the whole state variable would become a single value, no longer having both a first and last name. 

```javascript
function handleUserChange(event) {
    const key = event.target.id
    const value = event.target.value
  //setFirstName(event.target.value);
    setUserObj({ ...userObj , [key]: value })
}

```

In the event handler, we can grab the target element's id and set it as our key since they match the keys of our `userObj`. Likewise we can save our target element's value to a variable as well. Finally, we invoke our setter function `setUserObj` with argument `{...userObj , [key]: value }`. We use the spread operator to copy our current state of the userObj and use our `key` and `value` pair to update our desired object property. Doing so will update only our intended object while leaving all others in their current state. 

Creating one state variable object can help organize your code and make it easier to add and track additional fields to your form.

#Why Use Controlled forms?
Now you know how to create controlled forms, but why use them? One common use is for data validation.

###Validation
Users often make mistakes while filling out forms. But controlled forms allow us to catch these mistakes and alert the user so as to allow the application to be used as intended. In our previous example, we had inputs where we intended to capture a user's first and last name. But suppose a user inputted a number for their first name. Our code as it was would allow that input and we could end up with a user who's name appears as "34Mmy 5m1th". As l33t as that is, we may not want to allow users to do this.

Controlled forms can validate the data a user enters before actually setting state. 
```javascript
import React, { useState } from "react";

function Form() {
    const [userObj, setUserObj] = useState({firstName: "John" , 
                                            lastName: "Henry"})
function handleUserChange(event) {
    if (event.target.value === "" || isNaN(event.target.value)){
    const key = event.target.id
    const value = event.target.value
    setUserObj({ ...userObj , [key]: value })
} 
    else{
    console.log("Input is not valid")
}
}
  return (
    <form>
   <input type="text" id = "firstName" onChange={handleUserChange} value={userObj.firstName} />
<input type="text" id = "lastName" onChange={handleUserChange} value={userObj.lastName} />
      <button type="submit">Submit</button>
    </form>
  );
}
```
Within our `handleUserChange` function, we added an `if else` statement to validate our target's input. In this case, we allow our `userObj` to update if the input's value is an empty string or not a number. Otherwise, we print out an error to our console. You could write an alternative else statement to allow for some notification to the user, but for this simple example we will leave it with a simple `console.log` for the dev.