Forms with React Hooks
Tags: javascript, react, hooks
Published: 2022-09-09 06:57:35

## React Hooks
In a nutshell, React Hooks can be described as ;

> Functions that let you “hook into” React state and lifecycle features from function components. — [React](https://reactjs.org/docs/hooks-overview.html)

Introduced in React 16.8, a Hook (in React) is a function that keeps track of the state of a React functional component, and is used to solve the problem of only class components in React being able to access the state of the application before Hooks were introduced.

And **state** is how the dynamic data of a component is tracked by a component so that it can maintain the changes made to the component between renders.

So, to sum up, state stores the changing data of a component, and Hooks are used to manage that state.

This would mean that Hooks are essential for interactive components.

To start off, let’s see how we could use react hooks to keep track of the changing state in a vital interactive web component, forms, using two [Material UI](https://mui.com/) components to demonstrate, a select and a checkbox.

For the second part, a few more components will be added to the form to demonstrate how the code can be cleaned. And for the sake of an interesting example, the final form tries to figure out which type of dog matches a user’s personality. 🤭

## Handling form state
Handling a form’s state would mean specifying what will happen when a user changes an input field in the form, and keeping track of each change the user makes in the form.

### A Select component
To start, here’s the basic structure of Material UI’s Select component.

```js
<Select
value={}
onChange={}
label="On a typical weekend, I'll be"
labelId="select-outlined-label"
>
  <MenuItem value={3}>Bungee jumping, if I had the chance</MenuItem>
  <MenuItem value={2}>Taking a calm walk</MenuItem>
  <MenuItem value={1}>Taking a well deserved nap</MenuItem>
</Select>
```

Each `<MenuItem>` tag denotes an item in the select’s drop down list. And the value property on the `<MenuItem>` tag holds the data that identifies each option. The `value` property of the `<Select>` tag will be the value of the option picked by the user, and will therefore be used to display the option picked by the user.

Therefore, the data in the `value` property of the `<Select>` tag is what should be tracked as state, since the user selecting an option is the dynamic (changing) aspect of this form input field. Which means a variable to hold state should be passed into the `value` property of the `<Select>` tag. And the `onChange` property defines what will be done when the Select input field is changed. In this case, we would want it to update the data stored in the value property of the `<Select>` tag.

### The useState Hook
The `useState()` Hook is used to create a state holder required by the Select.

```js
const [activeScore, setActiveScore] = useState(3)
```

To break down that statement;

The argument passed into `useState()` is what the initial value of the state holder created will be. In this case the initial value would be 3. This means that the Select will initially display the menu item with a `value` property of 3 (`value={3}`) as picked by the Select component.

`[activeScore, setActiveScore]` uses array de-structuring here. The `useState()` function returns an array of two items: 
- The state holder
- A reference to a function that is used to update the value held in the state holder.

When array de-structuring is used here, the first element of the array returned (state holder) is stored in the variable `activeScore` and the second element of the array (a reference to update function) is stored in `setActiveScore`.

The variable names that are used when de-structuring the array could be anything, in this case they are named `activeScore` and `setActiveScore` to represent the state value they will be referring to.

The previous code for Select had data for its `value` and `onChange` property missing.

So, to pass the de-structured array to each prop, the `value` property will receive `activeScore` since it holds the value selected by the user for active score. To ensure that the `activeScore` will preserved when the page re-renders, the `onChange` property will receive `setActiveScore` because each time the user changes the value of Select, the new value will have to be updated in the `activeScore` state holder, and `setActiveScore` contains the reference to the function that does that.

But the each time the field is changed, an event object will be returned, which will contain the value of the value property of the `<MenuItem>` that the user has picked from the Select. This is the value that should be passed into `setActiveScore` and to extract this value, the `onChange` will be defined as `(e) => setActiveScore(e.target.value)` where `e` is the variable defined to store the event object received.

And the completed Select component with all its properties set to display and update the state, will look like this:

```js
<Select
value={activeScore}
onChange={(e) => setActiveScore(e.target.value)}
label="On a typical weekend, I'll be"
labelId="select-outlined-label"
>
  <MenuItem value={3}>Bungee jumping, if I had the chance</MenuItem>
  <MenuItem value={2}>Taking a calm walk</MenuItem>
  <MenuItem value={1}>Taking a well deserved nap</MenuItem>
</Select>
```

And the Select component rendered like this:

![The option to which activeScore is initialized to is displayed initially](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3icx9uu3indgwskzhr98.png)
<figcaption align = "center">The option to which activeScore is initialized to is displayed initially</figcaption>


### A Checkbox component

The only difference in this component is that it simply stores a Boolean value, therefore its state will be initialized to a true or false value unlike the previous state holders which were initialized by numbers.

```js
const [agree, setAgree] = useState(false);
```

And used in the Checkbox component in the following way;

```js
<FormControlLabel
label={
"I agree to not be offended by the results as this is just an " +
"example and not at all an accurate representation of me or the dog
😁"
}
control={
  <Checkbox
  checked={agree}
  onChange={(e) => setAgree(e.target.checked)}
  color="primary"
  />
}
/>
```

To demonstrate how state is dynamically updated, let’s enable the submit button only when the check box is checked.


![Image of enabled button](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jllwhc5u92cqp3auz8yo.png)
<figcaption align = "center">Box checked so button is enabled
</figcaption>


![Image of enabled button](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mo9vy142gcw084exrqpd.png)
<figcaption align = "center">Box unchecked so button is disabled</figcaption>

This can be done easily by passing the `agree` state holder manipulated by the checkbox to the `disabled` property of the Button component. When `agree` is true, `disabled` should be false. The logical NOT operator **!** is used with `agree` to get the logical complement (the opposite Boolean value) of `agree`.

```js
<Button
disabled={!agree}
variant="contained"
color="primary"
onClick={displayResults}/* displayResults is the function that determines the result from the user input */
>
  Show me the result
</Button>
```

Now with our form in place, we will be moving to the final piece of the page, the result display.


![Image of Result shown below the button](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r7x2o982aorpw9fso93i.png)
<figcaption align = "center">Result shown below the button</figcaption>

The component is nothing more than text below the button, but it will have to display a different result for each different combination of answers according to the `displayResults` function. 
Sounds familiar? Yup, we will be tracking state here as well.

First the state holder for the result, with the initial result set to nothing:

```js
const [result, setResult] = useState("");
```

Next, a [Typography](https://mui.com/material-ui/react-typography/) component to show the result and update each time `result` changes.

```js
<Typography variant="h4">
  {  result ? `It's the ${result}` : ``  }
</Typography>
```

The ternary operator `(<variable> ? <if true> : <if false>)` used here says that if `result` is true, the sentence is shown, else an empty string is visible. The sentence itself is a [template literal](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) that displays the value stored inside `result` with `${result}`.

When the submit button is clicked, the `displayResults` function is called and it changes result each time, by calling `setResult` within `displayResults`.

The `displayResults` function of this example is just a function that categorizes a user to a personality based on the scores chosen and can be replaced by any function that processes form data.

## Cleaning up the Hooks

A few more components like a slider and a radio group were added to this form to show how a lot of useState hooks could be a problem. 

With a couple more input fields, the useState hooks now look like this:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vlfv5dupakb7pv3ev1a3.png)

And having a separate useState hook for each input won’t look any better when the number of input fields increase.
Enter the useReducer hook.

### The useReducer Hook

This hook can be used alternative to the useState hook when managing complex state logic in a React component.

The `useReducer()` Hook will be given a reducer function and an initial state, the Hook returns an array with the current state and a function to pass an action to and invoke, in this case when an input field changes. The current state object and dispatch function are de-structured and stored in `state` and `dispatch` respectively.

```js
const [state, dispatch] = useReducer(reducer, initialFormState);
```

The `initialFormState` is the original state of the form and will there for have all the values passed into the `useState()` Hooks earlier, combined as one object.

```js
const initialFormState = {
  alertScore: 4,
  friendlyScore: 100,
  activeScore: 3,
  agree: false,
};
```

The `reducer` function passed into `useReducer()` returns some data, in this case an updated state object, and it will update the state based on the passed in action’s type. So, the reducer accepts a state (the current state) and an action.

```js
const reducer = (state, action) => {
switch (action.type) {
   case "activeScore":
    return { ...state, activeScore: action.value };
   case "alertScore":
    return { ...state, alertScore: action.value };
   case "friendlyScore":
    return {  ...state, friendlyScore: action.value };
   case "agree":
    return { ...state, agree: action.value };
   default:
    throw new Error();
  }
};
```

A switch case block inside determines how the state is updated, based on the `type` property of the `action` passed in.

So, if an action of, for example, `activeScore` is given, the `activeScore` property of the current state state is updated with the `value` property of the action `action.value` and a new state is returned.

```js
return { ...state, activeScore: action.value };
```

But, to **only** update the required property of the `state` object, the state is spread with the [spread operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) `...` .

The syntax expands the current object (`state`) so that all its current elements are included and the required element (`activeScore` in this case) is updated while preserving the original value of the other elements.

To capture user input, the `reducer` function is accessed by invoking the dispatch function returned from the `useReducer()`, in the `onChange` event of an input field.

Here’s the opening tag of the Select component doing that:

```js
<Select
  value={state.activeScore}
  onChange={(e) => dispatch(e.target)}
  //more component properties . . . 
>
```

Notice that the value of `<Select>` is now set to `state.activeScore` since we’re accessing the state of `activeScore` from the property stored in the current state object, `state`.

### Simplifying the reducer function

The `useReducer()` implemented now works. But, a case statement is needed for each action type (i.e.: each input field) that exists, which would be bad news for large forms.

However, notice how all the action types have the same name as the state element it controls.


![Image of action types having the same name as the state element it controls](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/87uwx5zpveyolrxhzwwb.png)

Therefore, the reducer can be simplified to update the state element that _has the same name_ as the `action.type` passed into it.

And the entire reducer function can be simply written as:

```js
const reducer = (state, action) => {
  return { ...state, [action.type]: action.value };
};
```

where the `[action.type]` is the required property to updated in the state.

And the onChange functions will be updated to:

```js
<Select
  value={state.activeScore}
  onChange={(e) => dispatch(
   {
      name: "activeScore",
      value: e.target.value,
   }
  )}
  //more component properties . . .
>
```

This dispatch call from onChange could be simplified further too.

Right now, the object passed into dispatch() is hardcoded for each input component.

But if we could retrieve the name and value both from the target property of event e itself, the dispatch() function would only require e.target to be passed into it. This is possible by giving a name property to <Select>.

```js
<Select
name="activeScore"
value={state.activeScore}
onChange={(e) => dispatch(e.target)}
//more component properties . . .
>
```

This way, both `name` and `value` can be extracted from `e.target`.

And to make the `reducer` function extract `name` from the action (`e.target`) passed in, the reducer will be updated the following way:

```js
const reducer = (state, action) => {
  return { ...state, [action.name]: action.value };
};
```

so that name is extracted from `action` instead of `type`.

And that’s how the :

- `useState` Hook can be set up to manage state.

- `useReducer` Hook can be used to manage large state objects, as an alternative to the `useState` Hook.

- `reducer` function of the `useState` Hook can be cleaned up a bit 😊.

---
Hope this example helped shed some light on React Hooks and how they are used in interactive web components to manage their state.

**_Till next time, happy coding!_**