React Series: useReducer
Tags: webdev, javascript, beginners, react
Published: 2023-04-15 17:53:23

useState hook as we discussed in one part of the series, helps in handling state management. However, useState is used for simple state management. When the state gets complex, it becomes uneasy to manage and can get quite confusing. 

In useState, we all know that the _state_, _callback_, and _rendering_ are all in the body of the component. Now, imagine if we have to manage several _form input_ states in our React application with different logics for each, the component gets larger, bulky and error-prone. This type of situation can break our application at the slightest update or refactoring of code. This is when the _useReducer()_ hook comes to play.

In this tutorial, I will elaborate on the _useReducer_ hook: what it does, how it works and other related questions that may pop up in your mind.

## useReducer()
useReducer is the hook that helps you add a _reducer_ to manage state in your component. Are you thinking "_What is a reducer _" ?. A _reducer_ is a function that helps you update the state. You don't get it still? Don't worry, we are in this together. As we dive deeper, you'll get a full understanding of this hook and the different concepts around it.

The hook is just like the _useState()_ hook, only that it helps manage _complex state_ situations. When you have to manage several states, the _useReducer()_ offers a more robust environment to manage the logic of your state.

While in _useState_, the whole body of work: logic, rendering etc is lumped into the component, _useReducer_ offers a different playing field for each aspect of the component. With this, it's easier to read the code, manage the state and render the component. As we dive deeper into the course, you will understand it a bit further than it sounds now.

Now, let's move on to the technicalities of our discussion.

## Important Concepts to Note
Honestly, you'll come across some concepts and words that may likely sound strange to you, but believe me, they aren't too difficult to grasp as they sound. You'll come across them in every step of your state management with _useReducer_.

Like _useState()_ where you declare a state in an array. The first being the _initialState_ and the second being the _updater function_.
`const [state, setState] = useState("")`.

With _useReducer_, it isn't much different. To declare a _reducer_, you do this:

```
import React, { useReducer } from 'react'

const [state, dispatch] = useReducer(reducer, initialState)
```
Now, let's discuss these concepts and arguments.

_**state**_: This is the current state. It is where the _initialState_ is triggered at the first render.
_**dispatch**_: This is the function that helps you update your current state from its _initialState_ to a new value.  It fires the action type that was declared for the update. Once this is done, the component re-renders as the state has been updated.
**_reducer_**: This is the reducer function that specifies how the state is updated. It takes in two arguments: _state_ and _action_. The state is the current state in question while the action is the type of activity that will be performed to update the state. I know this is a bit hard to grasp, but I'll explain with a counter app for a better understanding.
_**initialState**_: This is the initial state. It usually comes as an object holding the initial value of the state.

Now, let me use a simple counter app to explain these concepts, as I do realize it wouldn't be easy to grasp without a visible example. Let's get into it.

```
import React, { useReducer, useState } from 'react'

const initialState = 0
const reducer = (state, action) => {
    switch (action) {
        case "add":
            return state + 1;
        case "subtract":
            return state - 1;
        case "reset":
            return initialState;
        default:
            throw new Error("Invalid action")
    }
};

const Test = () => {
    const [state, dispatch] = useReducer(reducer, initialState)

  return (
     <div>
      <h2>{state}</h2>
      <button onClick={() => dispatch("add")}>
        add
      </button>
      <button onClick={() => dispatch("subtract")}>
        subtract
      </button>
      <button onClick={() => dispatch("reset")}>
        reset
      </button>
    </div>
  )
}

export default Test
```
In our _counter app_, we can see all the concepts and arguments I explained earlier in play. They all fit in one place or the other carrying out their function in making sure the app works as it should. 

Let's start from the top of the code.

We can see the _initialState_ being initialized. In most cases, it usually comes as an object, but in our case, it is a number. It all depends on the type of state or operation you're trying to update. Instead of having to initialize the _initialState_ to a value of `0` at the top of the code, this would work too;
`const [state, dispatch] = useReducer(reducer, 0)`.

Next, the _reducer_ function. It took in two (2) arguments: _state_ and _action_. The state being the _initialState_, and the _action_ being the type of operation we intend on carrying out on our counter app. We can see several action types(add, subtract, reset) that we could carry out on the application. 

Then in the JSX, we can see the _dispatch_ rolling out our _action types_ in the button to update the state of our _counter_. 

Now, I am going to refactor the code so you can choose what way to write it. It all depends on your choice.

```
import React, { useReducer, useState } from 'react'

const initialState = {count:0}
const reducer = (state, action) => {
    switch (action.type) {
        case "add":
            return {count: state.count + 1};
        case "subtract":
            return {count: state.count - 1};
        case "reset":
            return {count: initialState.count};
        default:
            throw new Error("Invalid action")
    }
};

const Test = () => {
    const [state, dispatch] = useReducer(reducer, initialState)

  return (
     <div>
      <h2>{state.count}</h2>
      <button onClick={() => dispatch({type: "add"})}>
        add
      </button>
      <button onClick={() => dispatch({type: "subtract"})}>
        subtract
      </button>
      <button onClick={() => dispatch({type: "reset"})}>
        reset
      </button>
    </div>
  )
}

export default Test
```

You can copy the code and try it out. It works just fine.

## When to Use useReducer

As I discussed at the beginning of the tutorial, _useReducer_ is best applicable when you have multiple or complex state situations. I will show an example of a form where we need to add a new user to our list of users. I'll show a _useState_ case and then apply the _useReducer_ case. 

```
import React, { useState, useEffect } from 'react'

const Test = () => {

    const [name, setName] = useState("")
    const [age, setAge] = useState()
    const [address, setAddress] = useState("")
    const [occupation, setOccupation] = useState("")
    const [maritalStatus, setMaritalStatus] = useState("")

    const handleForm = (e) => {
        e.preventDefault();
        setName(name)
        setAge(age)
        setAddress(address)
        setMaritalStatus(maritalStatus)
        setOccupation(occupation)
    }   
  return (
      <div>
          <form onClick={handleForm}>
              <h3>Employee Information</h3>
              <div>
                  <label htmlFor='name'>Name</label>
                  <input type='text' value={name} 
                   onChange={(e)=> setName(e.target.value)}/>
              </div>
              <div>
                  <label htmlFor='age'>Age</label>
                  <input type='number' value={age} 
                   onChange={(e)=> setAge(e.target.value)}/>
              </div>
              <div>
                  <label htmlFor='address'>Address</label>
                  <input type='text' value={address} 
                   onChange={(e)=> setAddress(e.target.value)}/>
              </div>
              <div>
                  <label htmlFor='occupation'>Occupation</label>
                  <input type='text' value={occupation} 
                   onChange={(e)=> setOccupation(e.target.value)}/>
              </div>
              <div>
                  <label htmlFor='maritalStatus'>maritalStatus</label>
                  <input type='text' value={maritalStatus} 
                   onChange={(e)=> setMaritalStatus(e.target.value)}/>
              </div>
              <button>SUBMIT</button>
          </form>
    </div>
  )
}

export default Test
```
This is a typical form using _useState_. In this form, we have 5 state values that need to be updated. You can see how clogged the component is.

Now we are going to use the _useReducer_ approach below to update our states, and you can have a clearer understanding of the slightest reason why this may be better.

```
import React, { useState, useEffect, useReducer } from 'react'

 const formState = {
        name: "",
        age: "",
        address: "",
        occupation: "",
        maritalStatus: ""
    }

    const employeeReducer = (state, action) => {
        switch (action.type) {
            case "addNewName":
                return {
                    ...state, name: action.payload
                }
            case "addNewAge":
                return {
                    ...state, age: action.payload
                }
            case "addNewAddress":
                return {
                    ...state, address: action.payload
                }
            case "addNewOccupation":
                return {
                    ...state, occupation: action.payload
                }
            case "addNewMaritalStatus":
                return {
                    ...state, maritalStatus:action.payload
                }
            default:
                return state;
        }
    }

const Test = () => {

    const [state, dispatch] = useReducer(employeeReducer, formState)
    
    const handleForm = (e) => {
        e.preventDefault();
        console.log(state);
    };
    

  return (
      <div>
          <form>
              <h3>Employee Information</h3>
              <div>
                  <label htmlFor='name'>Name</label>
                  <input type='text' name='name' value={state.name}
                      onChange={(e) => dispatch({type: "addNewName",             payload:e.target.value})} />
              </div>
              <div>
                  <label htmlFor='age'>Age</label>
                  <input type='number' name='age' value={state.age}
                       onChange={(e) => dispatch({type: "addNewAge", payload:e.target.value})} />
              </div>
              <div>
                  <label htmlFor='address'>Address</label>
                  <input type='text' name='address' value={state.address}
                       onChange={(e) => dispatch({type: "addNewAddress", payload:e.target.value})} />
              </div>
              <div>
                  <label htmlFor='occupation'>Occupation</label>
                  <input type='text' name='occupation' value={state.occupation}
                       onChange={(e) => dispatch({type: "addNewOccupation", payload:e.target.value})} />
              </div>
              <div>
                  <label htmlFor='maritalStatus'>maritalStatus</label>
                  <input type='text' name='maritalStatus' value={state.maritalStatus}
                       onChange={(e) => dispatch({type: "addNewMaritalStatus", payload:e.target.value})} />
              </div>
              <button onClick={handleForm}>SUBMIT</button>
          </form>
    </div>
  )
}

export default Test
```
I know you are thinking this method is much bulkier than using _useState_. Yes I agree, It's just an easier way to separate code into smaller chunks. There will arise times when you have to deal with much more state than these five and you'll find this method easier to update each state in real-time. 

If you find this tutorial helpful, don't forget to follow me and like it. There are other parts of the _React series_ on my page, you can go through them for your learning benefits.

Have any questions? Feel free to hola at me.

Please enjoy your weekend friends.