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 cart
button and list it in cart page. - Remove the product from cart list if the user click on
remove from cart
button. - 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-
cart
array property which holds products user added to his/her cart.2-
mode
property 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