The Trap of the useState hook with forms
Tags: react, javascript, webdev, frontend
Published: 2022-12-20 14:15:11



## A brief about the Series
In this Series of React What’s and Whatnots, we will discuss in a series of articles some of the patterns and mistakes that a lot of React developers - including myself - fall victim to.

## A brief about the Article
In this Article we will discuss a common mistake related to handling forms state.

## Project Setub
I have already prepared a [Github repo](https://github.com/AhmedSarhanSwvl/react-form.git) for you to follow along with,
So let’s start by opening a terminal where you feel comfortable and run the following commands: 

```bash
git clone https://github.com/AhmedSarhanSwvl/react-form.git 
cd react-form
npm install
npm run start
```

## Exploring our Application
Now we have our application up and running
As you can see, we have a simple five - six input form and a submit button.

![Form UI](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vaphgpgdv0t2xo96u3cv.png)
If we go back to the App.js file in our code, you will see this inside the jsx;
```react
    <form>
        <div className="form-group">
          <label htmlFor="username">Username</label>
          <input
            type="text"
            name="username"
            value={name}
            onChange={(e) => setName(e.target.value)}
          />
        </div>
        <div className="two-col_grid">
          <div className="form-group">
            <label htmlFor="email">Email</label>
            <input
              type="email"
              name="email"
              value={email}
              onChange={(e) => setEmail(e.target.value)}
            />
          </div>
          <div className="form-group">
            <label htmlFor="phone">Phone</label>
            <input
              type="tel"
              name="phone"
              value={phone}
              onChange={(e) => setPhone(e.target.value)}
            />
          </div>
        </div>
        <div className="two-col_grid">
          <div className="form-group">
            <label htmlFor="password">Password</label>
            <input
              type="password"
              name="password"
              value={password}
              onChange={(e) => setPassword(e.target.value)}
            />
          </div>
          <div className="form-group">
            <label htmlFor="confirmPassword">Confirm Password</label>
            <input
              type="password"
              name="confirmPassword"
              value={confirmPassword}
              onChange={(e) => setConfirmPassword(e.target.value)}
            />
          </div>
        </div>
        <div className="two-col_grid">
          <div className="form-group">
            <label htmlFor="city">City</label>
            <input
              type="text"
              name="city"
              value={city}
              onChange={(e) => setCity(e.target.value)}
            />
          </div>
          <div className="form-group">
            <label htmlFor="zipCode">Zip Code</label>
            <input
              type="text"
              name="zipCode"
              value={zipCode}
              onChange={(e) => setZipCode(e.target.value)}
            />
          </div>
        </div>
        <button type="submit">Submit</button>
      </form>
```
A very regular form inputs, but the problem lies in how the state of those inputs is being controlled;
If you scroll up to the very beginning of our App function you will see those useState hooks;

```react
 const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [phone, setPhone] = useState('');
  const [password, setPassword] = useState('');
  const [confirmPassword, setConfirmPassword] = useState('');
  const [city, setCity] = useState('');
  const [zipCode, setZipCode] = useState('');
```
And maybe for a small application or a small form that won’t be an issue but for a large piece of code this is not the best way to go, you will have tens of `useState` hooks in your file and this can get missy fast, it will be harder to read, harder to scale and harder to manage.
Generally speaking to sum the situation up:
if state updates independently - separate `useState`.
for state that updates together, or only one field at a time updates - a single `useState` object
for state where user interactions update different parts of the state - `useReducer`
so for our case, we would go with `useReducer`

UseReducer to the Rescue
React does offer another - yet somewhat similar to `useState` - hook, called `useReducer`

You can use it in your form as follows:
```react
function App() {
  const [formValues, setFormValues] = useReducer(
    reducerFunction,
    initialValues
  );
  ...
}
```

The `useReducer` hook like `useState` gives back two things; the state and the dispatcher and takes two arguments; the  reducer function  and the  initial values  of the reducer.
So we will now add the  initial values ; an object with the very initial values of our form state, and since both `initialValues` and `reducerFunction` are neither changing state nor dependent of any React Hooks, I’m going to create them outside the App component for better performance as follows:

```react
const initialValues = {
  name: '',
  email: '',
  phone: '',
  password: '',
  confirmPassword: '',
  city: '',
  zipCode: '',
};
const reducerFunction = (prevState, newState) => {
  return { ...prevState, ...newState };
};

function App() {
const [formValues, setFormValues] = useReducer(
    reducerFunction,
    initialValues
  );
...
}
```
Now what happens here, is that our `reducerFunction`  takes two arguments the `prevState` which is the previous state before the new change or what can be called the current state stored in the `useReducer` hook and the `newState` which is the new piece of state sent through `setFormValues` dispatcher coming from the `useReducer` hook in the change handler function as follow:

```react
 const onChangeHandler = (event) => {
    const { name, value } = event.target;
    setFormValues({ [name]: value });
  };
```

This function is going to be passed to ever input as a value to the onChange prop like this:

```react
<input type="email" name="email" value={email} onChange={onChangeHandler} />
```

Now this function takes the native event as argument and within the function we distructure the event to get the name and value of that input on every change.

We then dispatch the `setFormValues` function from the `useReducer` hook which in turn fires the `reducerFunction` to update the state

So to explain this briefly; for the email input when the value changes the `onChangeHandler` function fires taking the event as argument
When we distructure the event.target we get name and value which are ‘email’ and the new value of that email input
We then fire setFormValues({ [name]: value }) which translates to setFormValues({ email: emailValue })
Then in the reducer function which fires on that action dispatch 
It receives to arguments the prevState and the newState 
The prevState is state object of the whole form and the `newState` is what was passed to `setFormValues` i.e.

```react
prevState = {
    name,
    ...restOfState,
    email: oldEmailValue
  }
```
And

```react
newState = {
    email: newEmailValue
  }
```
In the `prevState`, `name, ...restOfstate` here refers to any state you have in the reducer other than the part relevant to us on this step "`email`"
And thanks to the destructuring ability of ES6, the new email value overrides the old email value and the state is updated

So we are left with one thing, how do we consume the values for the value prop of our inputs and the form submit function?

```react
  const { name, email, phone, password, confirmPassword, city, zipCode, } = formValues;
```
We will be destructuring the first value of the `useReducer` hook which is the `formValues`

So there you have it, how to replace multiple `useState` with one `useReducer` 😄🚀

Also please note that if your application does use multiple forms or face complex scenarios with the form validations and the state management of the forms, you can instead of `useReducer`, use some of the community packages that are more suited for such scenarios e.g. React-hook-form and Formik

Please let me know if you found this article useful and if you have any questions, leave them in the comments and I would be very happy to answer them.

The full code which you can also find on the [same repo](https://github.com/AhmedSarhanSwvl) in [final branch](https://github.com/AhmedSarhanSwvl/react-form/tree/final), will look like this

```react
import { useReducer } from 'react';
import './App.css';
 
const initialValues = {
  name: '',
  email: '',
  phone: '',
  password: '',
  confirmPassword: '',
  city: '',
  zipCode: '',
};
const reducerFunction = (prevState, newState) => {
  return { ...prevState, ...newState };
};
function App() {
  const [formValues, setFormValues] = useReducer(
    reducerFunction,
    initialValues
  );
 
  const onChangeHandler = (event) => {
    const { name, value } = event.target;
    setFormValues({ [name]: value });
  };
 
  const {
    name,
    email,
    phone,
    password,
    confirmPassword,
    city,
    zipCode,
  } = formValues;
  const submitForm = (e) => {
    e.preventDefault();
 
    const data = {
      name,
      email,
      phone,
      password,
      confirmPassword,
      city,
      zipCode,
    };
    console.log('data', data);
  };
 
  return (
    <div className="App" onSubmit={submitForm}>
      <form>
        <div className="form-group">
          <label htmlFor="name">Username</label>
          <input
            type="text"
            name="name"
            value={name}
            onChange={onChangeHandler}
          />
        </div>
        <div className="two-col_grid">
          <div className="form-group">
            <label htmlFor="email">Email</label>
            <input
              type="email"
              name="email"
              value={email}
              onChange={onChangeHandler}
            />
          </div>
          <div className="form-group">
            <label htmlFor="phone">Phone</label>
            <input
              type="tel"
              name="phone"
              value={phone}
              onChange={onChangeHandler}
            />
          </div>
        </div>
        <div className="two-col_grid">
          <div className="form-group">
            <label htmlFor="password">Password</label>
            <input
              type="password"
              name="password"
              value={password}
              onChange={onChangeHandler}
            />
          </div>
          <div className="form-group">
            <label htmlFor="confirmPassword">Confirm Password</label>
            <input
              type="password"
              name="confirmPassword"
              value={confirmPassword}
              onChange={onChangeHandler}
            />
          </div>
        </div>
        <div className="two-col_grid">
          <div className="form-group">
            <label htmlFor="city">City</label>
            <input
              type="text"
              name="city"
              value={city}
              onChange={onChangeHandler}
            />
          </div>
          <div className="form-group">
            <label htmlFor="zipCode">Zip Code</label>
            <input
              type="text"
              name="zipCode"
              value={zipCode}
              onChange={onChangeHandler}
            />
          </div>
        </div>
        <button type="submit">Submit</button>
      </form>
    </div>
  );
}
export default App;
```