React useReducer() 🪝
Tags: webdev, javascript, react, codenewbie
Published: 2023-02-08 18:28:26

useReducer() can serve as a replacement for useState() in scenarios where **more powerful state management is required**.

useState() is the primary state management hook in React and it works well when state updates are simple and limited to a handful of updates. useReducer() works better when you have related pieces of data/state.

Below is the syntax for calling the Reducer hook:
`const [state, dispatchFn] = useReducer(reducerFn, initialState, initialFn)`

useReducer returns an array. The first element within this array is a state snapshot used in the component re-render cycle. The second element is a function that can be used to dispatch a new action(trigger an update of the state). 

useReducer() takes 3 arguments. `reducerFn` is a function that is triggered automatically once an action is dispatched(via `dispatchFn`). It receives the latest state snapshot and returns the new, updated state. The second argument is the initial state (this is usually an object). `initFn` is a function to set the initial state programmatically.

Let's jump into some code to better understand these concepts 

---

### ⚔️ useState() vs useReducer() ⚔️

<br> <u>1. Building with useState()</u>

The code below creates an application where clicking on the button fetches 'posts' from a [JSONPlaceholder](https://jsonplaceholder.typicode.com/) and updates the states(there are three states - loading, post, error).

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

function App() {

  const [loading, setLoading] = useState(false)
  const [post, setPost] = useState({})
  const [error, setError] = useState(false)

  const handleRequest = () => {
    setLoading(true)
    setError(false)
    fetch("https://jsonplaceholder.typicode.com/posts/1")
    .then((res) => {
      return res.json()
    })
    .then((data) => {
      setPost(data)
      setLoading(false)
    })
    .catch((err) => {
      setError(true)
      setLoading(false)
    })
  }
  return (
    <div>
      <button onClick={handleRequest}>
        {loading ? "Wait..." : "Fetch the post"}
      </button>
      <p>{post?.title}</p>
      <span>{error && "Something went wrong!"}</span>
    </div>
  )
}

export default App
```

<br>
Let's first understand the code.

Inside the return block, we are displaying the post title and an appropriate message if there is an error.

Within `handleRequest`, the loading state is set to true. If the request is successful, I am updating the `post` state and setting the `loading` state as false. If the request failed, we are setting the `error` state as true and `loading` state as false.

The above example works as expected but on closer inspection you may notice how multiple states are updated together within a single function. Often, in such scenarios, replacing useState() with useReducer() is the preferred approach since we are updating multiple states together.
<br>


<u>2. Building with useReducer()</u>

Let's now try understanding how to implement the above application using useReducer().

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

const postReducer = (state,action) => {
  switch(action.type){
    case "FETCH_START": return{
      laoding: true,
      error: false,
      post: {}
    }
    case "FETCH_SUCCESS": return{
      loading: false,
      error: false,
      post: action.payload
    }
    case "FETCH_ERROR": return{
      loading: false,
      error: true,
      post: {}
    }
    default: return state
  }    
}

const INITIAL_STATE = {
  loading: false,
  post: {},
  error: false,
}

function App() {

  const [state, dispatch] = useReducer(postReducer, INITIAL_STATE)

  const handleRequest = () => {
    dispatch({type:"FETCH_START"})
    fetch("https://jsonplaceholder.typicode.com/posts/1")
    .then((res) => {
      return res.json()
    })
    .then((data) => {
      dispatch({type:"FETCH_SUCCESS", payload:data})
    })
    .catch((err) => {
      dispatch({type:"FETCH_ERROR"})
    })
  }
  return (
    <div>
      <button onClick={handleRequest}>
        {state.loading ? "Wait..." : "Fetch the post"}
      </button>
      <p>{state.post?.title}</p>
      <span>{state.error && "Something went wrong!"}</span>
    </div>
  )
}

export default App
```

`postReducer` is the reducer function that takes two arguments, `state` and `action`. `state` refers to the current state while `action` is used to pass information from the component to the reducer function. There are be three kinds of information that we would want to pass - 

_<u>Case 1</u>_ : When the button is clicked, the component informs the reducer that a fetch request is initiated. The reducer then updates the state as follows: 

> loading - true
> post - {}
> error - false

_<u>Case 2</u>_: If is JSON is fetched successfully, the component informs the the reducer and shares the response object. The reducer updates the state as follows:

> loading - false
> post - {id:1, title: _some_title_, desc: _some_description_}
> error - false

_<u>Case 3</u>_: If there is an error in fetching the JSON, the component passes this information to the reducer. The reducer then updates the state as follows:

> loading - false 
> post - {}
> error - true

The above example illustrates perfectly how useReducer() makes the complex multiple state updation process seamless. 👌. 

The reducer hook provides the ability to perform declarative state updates by decoupling state updates from the action that triggered the update.

<br>
The dispatch function allows us to send actions to the reducer. The reducer function essentially updates the state and returns the updated version of the state.

If you observe closely, the reducer function is written outside the scope of the component function. The reason behind this is because the Reducer function(`postReducer`) does not need to interact with anything defined within the component function. All information required inside the reducer function will be passed by React automatically.

---

It's completely understandable if the reducer hook got you feeling like this 👇

<img src="https://media.giphy.com/media/jRprTRTMUYv93xlxDL/giphy.gif">

<br>
useReducer() tends to be a complex hook to grasp but understanding its functionality will go a long way when learning _**Redux**_.