Exploring React Hooks: useReducer
Welcome back to our Exploring React Hooks series! In our previous article, we explored the useRef Hook. Today, we will explore another powerful hook called useReducer.
Let's begin..!!
🪝What is the useReducer Hook?
The useReducer hook is a powerful state management tool in React that simplifies complex state logic by utilizing a reducer function.
Syntax:
The syntax for useReducer is as follows:
const [state, dispatch] = useReducer(reducer, initialState);
state
represents the current state managed by useReducer.dispatch
is a function used to trigger state updates.reducer
is a function that receives the current state and an action and returns the new state.initialState
is the initial value of the state.
Working with Reducers:
A reducer is a pure function that takes in the current state and an action as parameters and returns the new state. It follows the following structure:
function reducer(state, action) {
switch (action.type) {
case 'ACTION_TYPE':
// Perform state transition and return the new state
default:
return state;
}
}
In the reducer function, we typically use a switch statement to handle different action types and define how the state should transition accordingly. You must remember to always return the current state as the default case to ensure that unhandled actions do not modify the state unintentionally.
Dispatching Actions:
To update the state managed by useReducer, we use the dispatch
function. This function takes an action object as an argument and triggers the execution of the reducer function.
dispatch({ type: 'ACTION_TYPE', payload: value });
The type
property is mandatory and represents the type of action being dispatched. Additional properties, such as payload
, can be included to pass any necessary data to the reducer.
🪝Example 1: Managing Simple State & Action
Let's create a component CounterReducer
. We'll set the initial state
of the counter to 0. Next, import the useReducer
Hook.
Inside the reducer
function, when the action is:
increment
: the state will be incremented by 1.
decrement
: the state will be decremented by 1.
reset
: the state will be reset to the initial value.
For any other action type, the current state will be returned as is.
Within the CounterReducer
component, the useReducer
hook is used to handle state management. It takes the reducer
function and initialState
as arguments and returns the current state (count
) and a function (dispatch
) to update the state by dispatching actions to the reducer.
import React, { useReducer } from 'react'
const initialState = 0
const reducer = (state, action) => {
switch (action) {
case 'increment':
return state + 1
case 'decrement':
return state - 1
case 'reset':
return initialState
default:
return state
}
}
function CounterReducer() {
const [count, dispatch] = useReducer(reducer, initialState)
return (
<div>
<div>Count = {count}</div>
<button onClick={() => dispatch('increment')}>Increment</button>
<button onClick={() => dispatch('decrement')}>Decrement</button>
<button onClick={() => dispatch('reset')}>Reset</button>
</div>
)
}
export default CounterReducer
In the JSX code, we render the current count value using {count}
. Three buttons are rendered: "Increment," "Decrement," and "Reset." Each button has an onClick
event handler that dispatches the corresponding action to the reducer when clicked.
The CounterReducer
component enables users to increment, decrement, or reset the counter value by dispatching different actions to the reducer. The reducer function processes these actions to determine the new state, which is then displayed in the output.
🪝Example 2: Managing Complex State & Action
Let's create another component CounterReducerTwo
. We'll set the initial state
of the firstCounter
to 0 and the secondCounter
to 1.
If the action type is "increment", the firstCounter
property in the state will get incremented by the value specified in the action
, while maintaining the other properties of the state using the spread operator.
Similarly, if the action type is "decrement", the firstCounter
property will get decremented by the specified value.
The "increment2" and "decrement2" actions follow a similar pattern but operate on the secondCounter
property instead.
And as before, if the action type is "reset", the state will be set back to the initialState
object. Also, if the action type does not match any of the defined cases, the current state is returned as is.
import React, { useReducer } from "react";
const initialState = {
firstCounter: 0,
secondCounter: 10
};
const reducer = (state, action) => {
switch (action.type) {
case "increment":
return { ...state, firstCounter: state.firstCounter + action.value };
case "decrement":
return { ...state, firstCounter: state.firstCounter - action.value };
case "increment2":
return { ...state, secondCounter: state.secondCounter + action.value };
case "decrement2":
return { ...state, secondCounter: state.secondCounter - action.value };
case "reset":
return initialState;
default:
return state;
}
};
function CounterReducerTwo() {
const [count, dispatch] = useReducer(reducer, initialState);
return (
<div>
<div>Count - {count.firstCounter}</div>
<button onClick={() => dispatch({ type: "increment", value: 1 })}>
Increment
</button>
<button onClick={() => dispatch({ type: "reset" })}>Reset</button>
<button onClick={() => dispatch({ type: "decrement", value: 1 })}>
Decrement
</button>
<div>Secound Counter - {count.secondCounter}</div>
<button onClick={() => dispatch({ type: "increment2", value: 5 })}>
Increment 5
</button>
<button onClick={() => dispatch({ type: "decrement2", value: 5 })}>
Decrement 5
</button>
</div>
);
}
export default CounterReducerTwo;
In the JSX code, we render the current count value of the firstCounter
using {count.firstCounter}
. Clicking the
The "Increment" button dispatches the action
{ type: "increment", value: 1 }
, incrementingfirstCounter
by 1.The "Decrement" button dispatches the action
{ type: "decrement", value: 1 }
, decrementingfirstCounter
by 1.
We also render the current count value of the secondCounter
using {count.secondCounter}
. Clicking the
The "Increment5" button dispatches the action
{ type: "increment2", value: 5 }
, incrementingsecondCounter
by 5.The "Decrement5" button dispatches the action
{ type: "decrement2", value: 5 }
, decrementingsecondCounter
by 5.
Clicking the "Reset" button dispatches the action { type: "reset" }
, resetting the state to the initialState
for both counters.
The rendered output shows the current values of firstCounter
and secondCounter
, and the counters can be incremented, decremented, or reset based on the corresponding button clicks.
🪝Benefits of useReducer:
Simplified State Management.
Centralized State Logic.
Testing and Debugging.
Compatibility with useContext.
To Summarize
The useReducer hook in React is basically an alternative to the useState Hook. We will explore the differences between useReducer and useState in an upcoming blog post.
The useReducer Hook simplifies state management by utilizing a reducer function to handle more intricate state transitions based on dispatched actions, providing a predictable and organized approach.
References
Thank's for taking the time to read this blog. Hope you found it informative and enjoyable! Connect with me on Twitter.
Stay tuned for our next article: useCallback Hook.
Catch you guys on the next one. Cheers .. ✌️
Subscribe to my newsletter
Read articles from Raj Sarkar directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Raj Sarkar
Raj Sarkar
Hi, I'm Rajarshi, a newbie blogger and a budding front-end developer. I started blogging to share my experiences and insights as a beginner in front-end development, as well as to connect with other developers and enthusiasts in the tech community.