How and when to use useReducer

1. useReducer
useReducer is a react hook used for state management. useReducer can be used as a substitute for useState. useState uses useReducer internally, demonstrating that it can perform all tasks that useState can.
useReducer takes in two arguments a reducer function and an initial state. The hook returns the dispatch function and the current state.
const [state, dispatch] = useReducer(reducer, initialValue)
The reducer function takes two parameters a current state and action. The action parameter dictates state transition. Action is responsible for changing the current state to the new state. The reducer function returns a single value which is our new state.
To understand the useReducer function better, we will look at the reduce() method in javascript, which shares similarities with the useReducer hook.
reduce() method takes in two parameters a reducer function and an initial value. The reducer function takes an accumulator and current value, which returns a single value.
const array1 = [1, 2, 3, 4, 5];
// 0 + 1 + 2 + 3 + 4 + 5
const initialValue = 0;
const reducerFunction = (accumulator, currentValue) => accumulator + currentValue
const sumWithInitial = array1.reduce(reducerFunction, 0)
console.log(sumWithInitial)
// expected output: 15
useReducer vs reduce
| useReducer | reduce |
Takes two parameters useReducer(reducer, initialState) | Takes two parameters array.reduce(reducer, initialValue) |
The reducer function takes in two parameters const reducer = (state, action) | The reducer function takes in two parameters const reducerFunction = reduce(accumulator, currentValue) |
| returns a two values [state, dispatch] | returns a single value |
2. Implementing a counter
We add our three buttons for the JSX:
- Increment
- Decrement
- Reset
<div>
<button>Increment</button>
<button>Decrement</button>
<button> Reset</button>
</div>
Import useReducer from react:
import React, { useReducer } from 'react';
Call our useReducer in the component:
const [state, dispatch] = useReducer(reducer, initialValue)
State and dispatch, which we obtained through array destructuring, are returned by useReducer.
Let us define our initial state outside the component:
const initialValue = {
count:0
}
InitialValue is an object whose count property has a value of 0.
Let us define our reducer function:
const reducer = (state, action) => {
switch (action.type){
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
case 'reset':
return initialValue;
default:
return state
}
}
The reducer function takes two parameters state and action. Action acts as an instructor to the reducer. The reducer executes the required state transition based on the action specified.
In our example, we have three actions;
- 'increment' - raises our state by 1.
- 'decrement' - decrements our state by 1
'reset' - resets our state to our initial state value
Our default case returns the state we're in right now.
To display the value of our current state, we use state:
<div> Count: {state.count}</div>
Each button gets an onClick event handler added. When a button gets clicked, we dispatch the action specified:
<div>
<div> Count: {state.count}</div>
<button onClick={() => dispatch({type: 'increment'})}>Increment</button>
<button onClick={() => dispatch({type: 'decrement'})}> Decrement</button>
<button onClick={() => dispatch({type: 'reset'})}> Reset</button>
</div>
The dispatch function takes an object as an argument. The object has a property name type with the value of the action specified.
3. Fetching data with useReducer
Install the axios package:
npm install axios
Import useReducer and useEffect in our file:
Import React, { useEffect, useReducer } from 'react';
Import the axios package in our file:
import axios from 'axios';
Call our useReducer in the component:
const [state, dispatch] = useReducer(reducer, initialState)
Define our initial state outside the component:
const initialState = {
loading: true,
data: {},
error: ''
}
The initial state is an object with three properties;
- loading set initially to true
- data which is an empty object
- error set to an empty string
Define the reducer function:
const reducer = (state, action) => {
switch (action.type){
case 'Success':
return {
loading: false,
data: action.payload,
error: ''
}
case 'Error':
return{
loading: false,
data: {},
error: 'Oops! Something went wrong'
}
default:
return state
}
}
Our reducer function has two actions to dispatch:
when the API call is successful:
- loading is set to false
- we add data to our object
- error is set to an empty string
When the API call fails:
- loading is set to false
- data is set to an empty object
- error is set to Oops! Something went wrong
We will use the cat facts API to consume data. Utilize the useEffect hook to make the API call.
useEffect(() => {
axios.get('https://catfact.ninja/fact')
.then((response) => dispatch({type:'Success', payload: response.data}))
.catch((error) => dispatch({type:'Error'}))
},[])
We use the axios package to make our API request in the useEffect. When the response is complete, we execute the dispatch function, which takes an object with two properties: an action of type Success and our payload for our data.
If the promise gets rejected, we dispatch an error action.
For our JSX, we determine whether loading is true; if it is, loading is displayed; if it is false, our data gets displayed.
Our error display message will be displayed if any error and loading is also set to false.
<div>
{state.loading? <p> Loading...</p>: state.data.fact}
{state.error && !state.loading? state.error: null}
</div>
4. When to use useReducer
UseReducer is better to useState in the following circumstances:
Please utilize useReducer if you are managing a large number of state transitions. It is simple to control because it occurs in one location. Improves the readability of the code you are writing.
When managing the state of an object or array.
When you manage related state transitions, for instance, in our example of data retrieval, we control the loading, data, and error in a single object.
Global state management when combined with the useContext hook. Enables state transfer to descendant components without the need for prop-drilling.
5. Conclusion
This article went in-depth on React’s useReducer Hook, going through its functionality and when to use it.
Please feel free to comment if you have any questions about this article.