DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Cover image for Guide to React hook- useReducer
Srishti Prasad
Srishti Prasad

Posted on

Guide to React hook- useReducer

What is useReducer Hook?

useState hook is not the only hook to manage state, useReducer is also used for the same. UseReducer just provides us with a mechanism to change a state based on the rules we provide, taking an initial state as input. That is simply useState dressed up.
useReducer hook is better than useState hook when we want to manage complex component states.

Syntax of useReducer and its comparison with useState syntax

Syntax which we have been using for useState() is:
const [ state, setState ] = useState( initialState )

For useReducer() we’ll be using
const [ state, dispatch ] = useReducer( reducer, initialState )

The useReducer hook takes three arguments including reducer, initial state, and the function to load the initial state.
Here reducer is the user-defined function that pairs the current state with the dispatch method to handle the state.

Since comparison helps to learn and estimate better, So useReducer will be more clear to you as I will be explaining by comparing it with useState hook.

From useState to useReducer

Since useReducer is the best solution in React for handling complex state interactions so let's look at how we can convert a component from useState to useReducer.

import { useState} from "react";

function App() {
 const [count, setCount] = useState(0);
 function increment(){
   setCount(count+1);
 }
 function decrement(){
   setCount(count-1);
 }
 return (
   <div className="App">
     <h1>Counter Value :{count}</h1>
     <button onClick={increment}>increase</button>
     <button onClick={decrement}>decrease</button>
   </div>
 );
}
export default App;


Enter fullscreen mode Exit fullscreen mode

In the above code we have a very simple counter component which can increment, decrement. In order to start converting this to use the useReducer hook we first need to remove the useState call and replace it with useReducer, but before we can do that we need to understand how useReducer is called.

UseReducer works similarly to useState in that it accepts a starting state as one of its arguments and provides us with the current state as well as a method for updating that state. Similar to useState, useReducer also re-renders a component when the state changes. The only significant distinction is that in order to modify our state, we must also give a reducer function to useReducer which we don't do in useState.

const [count, dispatch] = useReducer(reducer, 0)
In the above code you can see that the default state of 0 is passed as the second argument to useReducer and the count is returned as the first element in the array just like with useState. Now instead of having a setCount function we have a dispatch function which allows us to call the reducer function we pass to useReducer.

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return state+ 1
  case decrement:
      return state+ 1
    Default:
    return state
  }
}
const [state, dispatch] = useReducer(reducer,initialState)
Enter fullscreen mode Exit fullscreen mode

We now have defined the reducer function and it takes two parameters. The first parameter is the current state of our component. In our case this is just our count. The second parameter is our action which is going to be set to whatever you pass to dispatch. I will cover this more in just a bit. Now inside of the reducer function we have a set of defined actions we can perform on our state. In our case the only action we can perform is the increment action, so if we pass { type: 'increment } to dispatch then it will increase our count by one, otherwise the count will not change.

Modified code using useReducer()

import {useReducer } from "react";

const initialState=0;

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

function App() {
  const [state,dispatch]=useReducer(reducer,initialState);
 return (
   <div className="App">
     <h1>Counter Value :{state}</h1>
     <button onClick={()=>{
       dispatch({type:"increment"})
     }}>increase</button>
     <button onClick={()=>{
       dispatch({type:"decrement"})
     }}>decrease</button>
   </div>
 );
}
export default App;
Enter fullscreen mode Exit fullscreen mode

*When we click on increase button we want to dispatch an action *
But what happens if you want to provide your reducer some data? It's actually quite easy to do this. We may just add our data to the object we send to dispatch because we are free to pass anything we want to dispatch. The common practice is to put all your data inside a property called payload on your object. Here's an illustration of how to achieve it.

import { useReducer } from "react";

const initialState = 0;

function reducer(state, action) {
 switch (action.type) {
 case "increment":
     return state + 1;
   case "decrement":
     return state - 1;
   case "change-state":
     return state + action.payload.amount;
   case "reset":
     return 0;
   default:
     return state;
 }
}

function App() {
 const [state, dispatch] = useReducer(reducer, initialState);
 return (
   <div className="App">
     <h1>Counter Value :{state}</h1>
     <button
       onClick={() => {
         dispatch({ type: "increment" });
       }}
     >
       increase
     </button>
     <button
       onClick={() => {
         dispatch({ type: "decrement" });
       }}
     >
       decrease
     </button>

     <button onClick={() => dispatch({ type: "reset" })}> Reset</button>

     <button
       onClick={() => {
         dispatch({ type: "change-state", payload: { amount: 5 } });
       }}
     >
       Add 5
     </button>
   </div>
 );
}
export default App;
Enter fullscreen mode Exit fullscreen mode

Simply adding a new section to our reducer to handle this new action was all that was required to add this new action. Then we added a call to dispatch to initiate that operation, and we provided it with a payload containing the quantity by which we wish to alter our state.

CONCLUSION

Simple state can be built up inside of a component using the useState function. However, it is usually best to switch to useReducer when state begins to become more complicated and is shared by several components since useReducer makes it simpler to create sophisticated state interactions without producing a huge complex mess of code.

Complete code is available here :https://codesandbox.io/s/agitated-antonelli-dezsfz?file=/src/App.js

If you have any questions, leave a comment and I'll do my best to respond.
Give this blog a like ❀️ if you found it helpful and follow me for more blogs like this.

Top comments (2)

Collapse
himanshupal0001 profile image
Himanshupal0001

So I'm learning about redux today and I can't see much diffrenece between them. I consider useReducer is disguised as redux.

Collapse
srishtikprasad profile image
Srishti Prasad Author

Yaa @himanshupal0001 , the only difference which I see is that redux creates one global state container which is above your whole application whereas useReducer creates an independent component within your component itself.

Now it's your turn.

Β 
Join DEV and share your story.