So according to the official documentation of redux now redux toolkit is the only way to write redux logic ,as a matter of fact it was introduced due to the following three reasons :
- Configuring redux store is too complicated
- A lot of packages needed to be added to make redux logic work
- Also it requires too much boilerplate code
Let’s work with an application which make use of redux toolkit to manage the state of application .
So talking about the project it is a simple shopping cart in which we can add some products to the cart , we can remove products , we can increase the amount of products too and all this functionality will be achieved via redux-toolkit.
Firstly install redux-toolkit in your react application with the following command
npm install @reduxjs/toolkit
As we used to create store , exactly same way we are going to create store in our application
import { configureStore } from "@reduxjs/toolkit";
export const store=configureStore({
reducer:{
}
})
configureStore
accept an argument of reducer key which accepts all the slice reducers of the application and it combine all the slice reducer to form the root reducer , basically reducer is an object of the slice reducer .
Now if you don’t know what slice reducer is don’t worry by the end of this blog you will get familiar with all the jargon .
Once store is created now we need to make store available to the whole application so that any component can access the state of the application and how we are going to achieve this for that let's move to the main.jsx of our application where we have to wrap our App inside the provider
which can be imported from the react-redux and we need to pass store as props in Provider .
basically provider
is used to make store available to the nested components therefore it is passed at the top level this is the reason why we have nested our App inside Provider .
import React from 'react'
import ReactDOM from 'react-dom/client'
import { Provider } from 'react-redux'
import App from './App'
import './index.css'
import { store } from './store'
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
)
So our setup is done , now let’s learn how to create state with createSlice
API , think of it as one of the feature of your application , in our project we are managing the products of cart therefore slice logic revolve around products of cart only .
createSlice
object accepts an name key , initialstate value and object of reducer function , also it helps to manage redux related logic for a given slice in single file .
Therefore let’s create a feature folder under which create a bag folder and finally under the bag folder create a bag file
import { createSlice } from "@reduxjs/toolkit"
import bagItems from "../../data/data";
const initialState={
amount:0,
total:0,
bagitem:bagItems,
totalcart:[]
}
const bagSlice=createSlice({
name:'bag',
initialState,
})
export default bagSlice.reducer;
As you can see in above file a slice is created with the name bag and a initial state is passed with amount equal to zero which signify the amount of products in cart , total will be used to calculate the total price of cart , totalcart is kept empty as it will used for storing cart items and bagItems contains the product in our shopping app for which I have created the data which is imported from the data file . In order to access the data you can visit to the github repository of this project link for which is mentioned below .
One thing to note here is that bagSlice is an object which have action , reducer and some other properties from which we have exported reducer as shown in above file .
I have log the bagSlice in order to show you the object which clearly shows that bagSlice contain object of actions , reducer etc .
Remember in beginning we created a store in our application which take an argument of reducer object therefore in order to make this bag slice available to our whole application we need to export the slice reducer to our store .
import { configureStore } from "@reduxjs/toolkit";
import bagReducer from "./features/bag/bag.jsx"
export const store=configureStore({
reducer:{
bag:bagReducer
}
})
therefore in the store file we can see that we have passed the bagReducer imported from bag Slice to a key named bag .
Likewise if there are any other slice in your application needs to be added to the reducer object.
Now let’s move to our home page where we will just show the products name , image and their price and a button used to add products to cart .
Also we can look at the state in our redux devtool in console as shown below
Now let’s learn how to access state from slice in our components for this purpose we are going to import use Selector
in our home page, it is used to access the state from store.
import React from 'react'
import { useSelector } from 'react-redux'
const Home = () => {
const { bagitem } = useSelector((store) => store.bag)
console.log(bagitem);
return (
<div className='home'>
<div className="mid">
{
bagitem.map((item)=>{
return <Item key={item.id} {...item}/>
})
}
</div>
<div className='tact'>
<button ><Link to="/bag">
Go to Cart
</Link>
</button>
</div>
</div>
</div>
)
}
export default Home
As for now currently we have only one slice therefore in the above code we can see that we are accessing the bag slice from store and we are destructuring bag item from this .In order to check whether we are getting the data or not I have log the value of bagitem let’s check
therefore it can be clearly observed that we are able to access the value and our code is working fine up till now.
Our home page kinda look like this as shown below , in this tutorial we are going to keep our focus on the redux-toolkit only therefore if you want to check the code part of the user interface I have added the github link of this repository at the end .
So far we have covered how to create state and how to access the state value from store now let’s take one step ahead and create reducer function in our slice , reducer function contain the logic and calculation needed to be performed on state.
As you can see we have add to cart button in our app so when user click on this we need to add that product in our cart and to achieve this we need to create a reducer function let ‘s create it
import { createSlice } from "@reduxjs/toolkit"
import bagItems from "../../data/data";
const initialState={
amount:0,
total:0,
bagitem:bagItems,
totalcart:[]
}
const bagSlice=createSlice({
name:'bag',
initialState,
reducers:{
addtocart:(state,{payload})=>{
const bagItem=state.bagitem.filter((item)=>item.id===payload.id);
state.totalcart.push(bagItem[0]);
}
}
})
export const {addtocart}=bagSlice.actions
export default bagSlice.reducer;
One thing to note here is redux toolkit automatically uses immer library therefore we do not need to return new state in our reducer function we can make changes directly to the state
addtocart
is a reducer function i.e it takes previous state and action as an argument and return a new state with applied changes here I have destuctured the payload from action .
In the above code we can see that I have created a addtocart
function so what is happening in this function is that if a user click on the button add to cart that product is added to the totalcart which was initially kept to be empty .
Now we need to export this function in order to make use of it in components and we can export it by destructuring it from actions property of bagSlice.
Now we need to make use of this function in our component for this purpose let ‘s make use of usedispatch
it give us store dispatch method which basically affect the state of store .
import React from 'react'
import { useDispatch } from 'react-redux'
import "../App.css"
import { addtocart } from '../features/bag/bag';
const Item = ({ title, img, price, id }) => {
const dispatch = useDispatch();
return (
<table className="item">
<tr>
<td>
<img src={img} style={{ height: "50px", width: "50px" }} alt="" />
</td>
<td>
<p>{title}</p>
</td>
<td>
<p className='price'>${price}</p>
</td>
<td>
<button className='buts' onClick={() => dispatch(addtocart({ id }))}>Add To Cart</button>
</td>
</tr>
</table>
)
}
export default Item
dispatch takes the reducer function as an argument and it call the reducer function which finally make changes in the state .
In the above code we can see that we are importing usedispatch
from react-redux and we are making use of it in the button to access addtocart
function from state also we are passing the id as argument in function addtocart
because on the basis of this only selected product will be added to total cart .
Suppose if user add the first product in totalcart and then let’s check the state in redux devtool →the product can be viewed in totalcart
Also we can view the difference in our redux devtool
The highlight part shows that earlier the totalcart was empty now it contain a product .
Once done with the home page now let’s move to our cart page where we can explore other reducer function .
Our cart page look like this
Things to notice in cart page are that we can increase and decrease the amount of product , with the increase in amount of product the total price of cart is increasing with it which can be seen at bottom of cart page plus total amount of product is also increasing which can be seen at the navbar and we also have a remove item button to delete item from cart .
Now we need to create reducer function for all this functionality in our slice
import { createSlice } from "@reduxjs/toolkit"
import bagItems from "../../data/data";
const initialState={
amount:0,
total:0,
bagitem:bagItems,
totalcart:[]
}
const bagSlice=createSlice({
name:'bag',
initialState,
reducers:{
addtocart:(state,{payload})=>{
const bagItem=state.bagitem.filter((item)=>item.id===payload.id);
state.totalcart.push(bagItem[0]);
} ,
removeItem:(state,{payload})=>{
state.totalcart=state.totalcart.filter((item)=>item.id!==payload.id);
} ,
increaseitem:(state,{payload})=>{
const cartitem=state.totalcart.find((item)=>item.id===payload.id)
cartitem.amount=cartitem.amount+1;
},
decreaseitem:(state,{payload})=>{
const cartitem=state.totalcart.find((item)=>item.id===payload.id)
cartitem.amount=cartitem.amount-1;
},
totalamount:(state)=>{
let amount=0;
let total=0;
state.totalcart.forEach((item)=>{
amount+=item.amount;
total+=item.amount*item.price;
})
state.amount=amount;
state.total=total;
}
}
})
export const {addtocart,removeItem,increaseitem,decreaseitem,totalamount}=bagSlice.actions
export default bagSlice.reducer;
- removeitem→ it is a simple function where we are just deleting a element from array .
- increaseitem→ In this function we are increasing the amount of product
- decreaseitem → In this function we are decreasing the amount of product
- totalamount → In this function we are traversing through the whole array and we are calculating the total amount and total price of cart .
One thing to notice here is we are accessing totatlcart from state not bagitem because we are making changes in the cart .
Now we just need to dispatch these function in our cart as shown below
Parent component is the bag page
import React from 'react'
import { useSelector } from 'react-redux'
import Cart from '../components/Cart';
const Bag = () => {
const { totalcart, total } = useSelector((state) => state.bag);
console.log(totalcart)
return (
<div>
{ totalcart ? (totalcart.map((item) => {
return <Cart key={item.id} {...item} />
})) : (
null ) }
<br />
<hr />
<div className='total'>
<p>
total
</p>
<p>${total}</p>
</div>
</div>
)
}
export default Bag
the child component cart is shown below
import React from 'react'
import { useDispatch } from 'react-redux'
import { decreaseitem, increaseitem, removeItem } from '../features/bag/bag';
const Cart = ({ title, img, price, id, amount }) => {
const dispatch = useDispatch();
return (
<table className="item">
<tr>
<td>
<img src={img} style={{ height: "50px", width: "50px" }} alt="" />
</td>
<td>
<p>{title}</p>
</td>
<td>
<p className='price'>${price}</p>
</td>
<td>
<button onClick={() => dispatch(increaseitem({ id }))}>+</button>
{amount}
<button onClick={() => {
if (amount === 1) {
dispatch(removeItem({ id }));}
dispatch(decreaseitem({ id }))
}} >-</button>
</td>
<td>
<button onClick={() => dispatch(removeItem({ id }))}>Remove Item</button>
</td>
</tr>
</table>
)
}
export default Cart
Repository Link
https://github.com/2devyank/Redux-toolkit
Star this repository If you find it useful
Top comments (0)