useReducer() is a React.js Hook which manage complex state in your application and update it based on the 'action' you send to.
It's used as an alternative for useState if you have a complex states or can be used both together according to your application requirements.
It's very similar to Redux if you want to not use a 3rd-party library.
First
You should import useReducer from react js
import React, { useReducer } from 'react';
Second :
useReducer Hook accepts a reducer function, and an initial state.
It's returns an array with 2 values:
The first one is the state value, and the second value is the dispatch function which is used to trigger an action with the help of ES6 destructuring.
const [state, dispatch] = useReducer(reducer, initialState);
initialState :
The initial state that we want to start working on it
reducer :
Which we'll use to manipulate our state.
Let's go with simple example
Let's say you're displaying some products in your app, and you want to :
- Add a product to the cart when user click on
add to cartbutton and list it in cart page. - Remove the product from cart list if the user click on
remove from cartbutton. - User can switch the application (dark/light mode)
Create Reducer.js component to add our reducer in it.
How to use it ?
- Firstly, we will define the reducer function that will manipulate our state:
// Reducer.js
const reducer = (state, action) => {
// reducer function accepts two arguments
/*
the first one is `state` which is going to be the state
before the update.
*/
/*
the second one is `action` which is what are we trying to do.
*/
}
export default reducer;
- Second step, we will define the initial state object which contains our initial values :
1-
cartarray property which holds products user added to his/her cart.2-
modeproperty which holds the app mode and it's by default will belight.
We'll put those in our file, but outside of the component:
// Reducer.js
export const initialState = {
cart: [],
mode: 'light'
};
const reducer = (state, action) => {
switch (action.type) {
case 'ADD_TO_CART':
return {
...state,
cart: [...state.cart, action.item]
};
case 'REMOVE_FROM_CART':
const index = state.cart.findIndex(item => action.id === item.id);
let newCart = [...state.cart];
if (index >= 0) {
newCart.splice(index, 1);
}
if (newCart.length === 0) {
return {
...state,
cart: newCart,
}
} else {
return {
...state,
cart: newCart,
}
};
case 'CHANGE_MODE':
return {
...state,
mode: action.mode
}
default: return state
}
}
export default reducer;
Now, let's imagine we have a Product.js component that return a products to be displayed in your app.
// Product.js
import React from 'react'
function Product({ name, image }) {
const addToCart= () => {
// some code
}
return (
<div className="product">
<img src={`${image}`} />
<p>{name}</p>
<button onClick={addToCart}>add to cart</button>
</div>
)
}
export default Product
- The fourth step, our reducer is all setup, now let's define then use it. To be able to use the reducer function you must define it in everywhere you need like so:
import React, {useReducer} from 'react'
import reducer, {initialState} from './Reducer';
function Product({ name, image }) {
const [state, dispatch] = useReducer(reducer, initialState) ;
.
.
.
Firstly you need to import useReducer from React, then import the reducer and initialState to be used.
Now we will define our reducer using useReducer() hook
const [state, dispatch] = useReducer(reducer, initialState) ;
The useReducer hook will return two things in an array: the state, and a dispatcher to update the state.
We will grab those with array destructuring, similar to state and setState with the useState.
Dispatch actions :
Every time the user clicks on add to cart button, addToCart function will be called to dispatch the action to the reducer function to do some changes in that state.
So, the addToCart function will contains the following code :
const addToCart= () => {
dispatch({
type: 'ADD_TO_CART',
item: {
image,
name
}
})
}
We sent the action name to the reducer function to know what kind of changes will be happened, in this case the action is ADD_TO_CART action. Also we sent the item or the product which user need to add to the card to be added to the state.cart array.
So, the Product.js component will be :
import React, {useReducer} from 'react'
import reducer, {initialState} from './Reducer';
function Product({ name, image }) {
const [state, dispatch] = useReducer(reducer, initialState) ;
const addToCart= () => {
dispatch({
type: 'ADD_TO_CART',
item: {
image,
name
}
})
}
return (
<div className="product">
<img src={`${image}`} />
<p>{name}</p>
<button onClick={addToCart}>add to cart</button>
</div>
)
}
export default Product
We will be satisfied with ADD_TO_CART, others actions will be the same but with deferent functionality
Accessing state
Now you can accessing the state and make on it anything you want like mapping cart array and so on.
Conclusion
useReducer Hook is extremely useful when working on complex and different states depend on each other.
useReducer is very similar to Redux if you want to not use a 3rd-party library or if it's only for a component or two.
Hope this article helped you to understand useReducer hook what is exactly.
Thanks for reading 🙌
-Ahmad Mukahal
Top comments (2)
really great explanation! Thanks!
Thank you and good luck