A Complete Beginner's Guide to useReducer Hook
Tags: javascript, beginners, react
Published: 2021-08-05 11:03:54

<br />
## Introduction
<br /> 
>useReducer is a React Hook introduced late in October 2018, which allows us to handle complex state logic and action. It was inspired by Redux state management pattern and hence behaves in a somewhat similar way.

Oh! but don't we already have a useState hook to handle state management in React? 

Well, yes! useState does the job pretty well.
However, 
the useState hook is limited in cases where a component needs a complex state structure and proper sync with the tree. useReducer when combined with useContext hook could behave very similarly to Redux pattern and sometimes might be a better approach for global state management instead of other unofficial libraries such as Redux.
As a matter of fact, the useReducer's API itself was used to create a simpler useState hook for state management.

***According to React's official docs :***

*"An alternative to useState. Accepts a reducer of type (state, action) => newState, and returns the current state paired with a dispatch method."*
<br />
## A call to Reduce Method in JavaScript 
<br />
To begin with useReducer, first, we need to understand how JavaScript's built-in Array method called Reduce works, which shares remarkable similarity with the useReducer hook.

The reduce method calls a function (a reducer function), operates on each element of an array and always returns a single value.


```javascript
function reducer(accumulator, currentvalue, currentIndex, sourceArray){
  // returns a single value
}

arrayname.reduce(reducer)
```

As stated, the above reducer function takes in 4 parameters -

**1.**  **_Accumulator_** : It stores the callback return values.

**2.**  **_Current Value_** : The current value in the array being processed.

**3.**  **_Current Index (optional)_** : The index of the current value in the array being processed.

**4.**  **_Source Array_** : The source of the array on which reduce method was called upon.


Let's see reduce function in action, by creating a simple array of elements :

```javascript
const items = [1, 10, 13, 24, 5]
```

Now, We will create a simple function called `sum`, to add up all the elements in the items array. The `sum` function is our reducer function, as explained above in the syntax

```javascript
const items = [1, 10, 13, 24, 5]

function sum(a,b, c, d){
    return a + b
}

```


As we can see, I am passing four parameters named `a, b, c, d`, these parameters can be thought of as `Accumulator, Current Value, Current Index, Source Array` respectively.


Finally, calling the `reduce` method on our `sum` reducer function as follows 


```javascript
const items = [1, 10, 13, 24, 5];

function sum(a, b, c, d){
    return a + b;
}

const out = items.reduce(sum);

console.log(out);

OUTPUT :
59

```
Let's understand what's going on here :

When the `reduce` method gets called on the `sum` function, the accumulator ( here `a`) is placed on the zeroth index (1), the Current Value (here b) is on `10`. On the next loop of the `sum` function, the accumulator adds up `a + b` from the previous iteration and stores it up in `accumulator` (a) while the current value(b) points to 13 now.
Similarly, the accumulator keeps on adding the items from the array whatever the Current Index is pointing until it reaches the very end of it. Hence resulting in the summation of all the items in the array.

```javascript
// We can do a console.log to check iteration in every function loop :

const items = [1,10,13,24,5];

function sum(a, b, c, d){
   console.log("Accumulator", a)
   console.log("Current Index", b)
 	 return a + b
}

const out = items.reduce(sum);

console.log(out);

'Accumulator' 1
'Current Index' 10
'Accumulator' 11
'Current Index' 13
'Accumulator' 24
'Current Index' 24
'Accumulator' 48
'Current Index' 5
53


```

In addition to this, there is an optional `initial value`, when provided will set the accumulator to the initial value first, before going out for the first index item in the array. The syntax could look like this :

```javascript
items.reduce(sum, initial value)
```


While we just finished understanding how the `reduce` method works in JavaScript, turns out both the Redux library and the `useReducer` hook shares a common pattern, hence the similar name.

> ![reduce v_s useReducer.png](https://functionalworks\-backend\-\-prod.s3.amazonaws.com/logos/80dbe88cb7e77aa1a80fc0f0ccfa219c)
<br />
## How does useReducer works?
<br />
useReducer expects two parameters namely, a reducer function and an initial state.

```
useReducer(reducer, initialState)
```
Again the reducer function expects two parameters, a current state and an action and returns a new state.

```
function reducer(currentState, action){
	returns newState;
}
```
<br />
## useReducer Hook in Simple State and Action
<br />
Based on what we have learnt so far, let's create a very simple counter component with increment, decrement feature.

We will begin by generating a JSX component :
```
import React from 'react';
import ReactDOM from 'react';

function App(){
  return (
    <div>
        <button onClick={handleIncrement}>+</button>
        <span>{state}</span>
        <button onClick={handleDecrement}>-</button>
    </div>
  );
}
// define a root div in a HTML file and mount as such
ReactDOM.render(<App />, document.getElementById("root"));

``` 
Create a `reducer` function, expecting a state and an action. Also, attach `onClick` events on both buttons and define the `click` functions within the `App` component :


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

function reducer(state, action){
  // return newState
}


function App(){
  function handleDecrement() {
    // ...
  }

  function handleIncrement() {
    // ...
  }

return (
  <div>
    <button onClick={handleIncrement}>+</button>
    <span>{state}</span>
    <button onClick={handleDecrement}>-</button>
  </div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
```

Moving onwards, before we trigger `useReducer` hook, it is important to note that it returns an array of two values,

```
const state = useReducer[0]
const dispatch = useReducer[1]
```

We can simplify the above, using array destructuring (a best practice)  :

```
const [state, dispatch] = useReducer(reducer, intialState)
```
Now, coming back to our counter component and including the above `useReducer` snippet in it



```
function reducer(state, action){
  if (action === "increment") {
    return state + 1;
  } 
  else if (action === "decrement") {
    return state - 1;
  } 
  else null;
}


function App(){
  function handleDecrement() {
    dispatch("decrement");
  }

  function handleIncrement() {
    dispatch("increment");
  }

  const [state, dispatch] = React.useReducer(reducer, (initialState = 2));

return (
  <div>
    <button onClick={handleIncrement}>+</button>
    <span>{state}</span>
    <button onClick={handleDecrement}>-</button>
  </div>
);
}


```
Link to [codepen](https://codepen.io/abhinav-anshul/pen/mdRmKdy?editors=0011)

The `handleIncrement` and `handleDecrement` function returns a dispatch method with a string called `increment` and `decrement` respectively. Based on that dispatch method, there is an if-else statement in the reducer function which returns a new state and eventually triggering (overriding in a way) the `state` in useReducer.

According to the official docs, always use `Switch` statements in the reducer function (you already know this if you have worked with Redux before), for more cleaner and maintainable code. Adding more to this, it is advisable to create an initial state object and pass a reference to the `useReducer` 

```
const initialState = { 
  count: 0 
  // initialize other data here
}
const [state, dispatch] = React.useReducer(reducer, intialState);

```

<br />
##useReducer Hook in Complex State and Action
<br />
Let's see the same counter component, building it with what we have learnt so far but this time with a little complexity, more abstraction, also best practices.


```
const initialState = {
  count: 0
};

function reducer(state, action) {
  switch (action.type) {
    case "increment":
      return { count: state.count + action.value };
    case "decrement":
      return { count: state.count - action.value };
  }
}

function App() {
  const [state, dispatch] = React.useReducer(reducer, initialState);

  return (
    <div>
      <button onClick={() => dispatch({ type: "increment", value: 5 })}>
        +
      </button>
      <span>{state.count}</span>
      <button onClick={() => dispatch({ type: "decrement", value: 5 })}>
        -
      </button>
    </div>
  );
}


```

Link to [codepen](https://codepen.io/abhinav-anshul/pen/vYgzzaa)

**What has changed?**

1. Instead of passing a value directly to the `useReducer` hook, we have an object initialised with a count property set to zero. This helps in cases when there are more than a single property to be initialised also easier to operate on an object.

2. As we discussed earlier, `if-else` has been modified to `switch` based statements in the reducer function.

3. The `dispatch` method is now object-based and provides two properties `type` and `value`. Since the dispatch method triggers `action`, we can switch statements in the reducer function using `action.type`. Also, the new state can be modified by using a dynamic value which can be accessed on `action.value`

<br />
##Dealing with Multiple useReducer Hook 
<br />
When dealing with multiple state variables that have the same state transition, sometimes it could be useful to use multiple `useReducer` hook that uses the same `reducer` function.

Let's see an example :

```
const initialState = {
  count : 0
}

function reducer(state, action) {
switch (action) {
    case "increment":
      return { count: state.count + 1 };
    case "decrement":
      return { count: state.count - 1 };
      default : 
      return state
  }
}

function App() {
  const [state, dispatch] = React.useReducer(reducer, initialState);
  const [stateTwo, dispatchTwo] = React.useReducer(reducer, initialState);

return (
  <>
    <div>
        <button onClick={() => dispatch('increment')}>+</button> 
        <span>{state.count}</span>
        <button onClick={() => dispatch('decrement')}>-</button>
    </div>
    <div>
        <button onClick={() => dispatchTwo('increment')}>+</button>
        <span>{stateTwo.count}</span>
        <button onClick={() => dispatchTwo('decrement')}>-</button>
    </div>
  </>
);
}



ReactDOM.render(<App />, document.getElementById("root"));



```
Here we are using two `useReducer` hook with different dispatch and state triggering the same `reducer` function.


Link to [codepen](https://codepen.io/abhinav-anshul/pen/vYgzqzg)

<br />
## useState v/s useReducer
<br />

Here is a quick rundown, comparing both the hooks :

![useState v_s useReducer.png](https://functionalworks\-backend\-\-prod.s3.amazonaws.com/logos/2e98eca325d391939958096227f254a6)

<br />
## useReducer's Rendering Behavior
<br />

React renders and re-renders any `useReducer` component very similar to the `useState` hook.

consider the following contrived example where `+` increments the `count` by 1, `-` decrements the `count` by 1 and `Reset` restores the `count` value to 0.

```javascript
function App(){
  const [count, dispatch] = useReducer(reducer, initialState)
  console.log("COMPONENT RENDERING");
    return (
      <div>
          <div>{count}</div>
          <button onClick={() => {dispatch('increment')}>+</button>
          <button onClick={() => {dispatch('decrement')}>-</button>
          <button onClick={() => dispatch('reset')}>Reset</button>	
      </div>
	)
}
```
The above `App` component :

**1.** Re-render every time the `count` state changes its value, hence logging out the `COMPONENT RENDERING` line.

**2.** Once, the reset button has been clicked, the subsequent clicks to the `reset` button won't re-render the `App` component as the state value is always zero.


<br />
<br />
While we just finished reading how rendering happens in the context of `useReducer`, we have reached the end of this article!


**Some Important Resources that I have collected over time:**

**1.**  [https://reactjs.org/docs/hooks-reference.html#usereducer](https://reactjs.org/docs/hooks-reference.html#usereducer)

**2.**  [https://geekflare.com/react-rendering/](https://geekflare.com/react-rendering/)

**3.**  [https://kentcdodds.com/blog/should-i-usestate-or-usereducer](https://kentcdodds.com/blog/should-i-usestate-or-usereducer)

**4.**  [https://kentcdodds.com/blog/application-state-management-with-react](https://kentcdodds.com/blog/application-state-management-with-react)


_Loved this post? Have a suggestion or just want to say hi? Reach out to me on_ [_Twitter_](https://twitter.com/abhhnv)

Originally written by [Abhinav Anshul](https://javascript.works-hub.com/users/c49b5fad-2fef-4acc-aa7a-7cd78f11c9a1?utm_source=dev_to&utm_medium=blog_xpost) for [JavaScript Works](https://www.works-hub.com/learn/a-complete-beginners-guide-to-usereducer-hook-f4a78?utm_source=dev_to&utm_medium=blog_xpost)