Wishlist is one of the important concepts of an E-commerce platform. In this article, we are going to learn how to implement Wishlist functionality using React JS and Redux Toolkit.
Preview of final output: Let us have a look at how the final output will look like.
Prerequisites
Approach to create Wishlist Functionality:
-
Install Redux Toolkit:Use npm or yarn to install Redux Toolkit:
npm install @reduxjs/toolkit
oryarn add @reduxjs/toolkit
. -
Create Wishlist Slice:Use
createSlice
to define a Redux slice for the Wishlist.Specify the initial state and create reducers for actions likeaddToWishlist
andremoveFromWishlist
. -
Combine Reducers:Combine the Wishlist reducer with other reducers, if any, using
combineReducers
. -
Configure Redux Store:Utilize
configureStore
to set up the Redux store and pass the combined reducer. -
Dispatch Actions in Components:Use
useDispatch
anduseSelector
hooks fromreact-redux
in components.Dispatch actions (e.g.,addToWishlist
,removeFromWishlist
) based on user interactions.
Steps to Create and Configure React App with redux toolkit
Step 1: Create a react app using command “npx create-react-app app-name”.
npx create-react-app app-name
Step 2: Install the required dependencies
npm install react-redux @reduxjs/toolkit
npm install react-hot-toast
npm i react-router-dom react-icons
Project Structure:
The updated dependencies in package.json file will look like:
"dependencies": {
"@reduxjs/toolkit": "^2.0.1",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hot-toast": "^2.4.1",
"react-icons": "^4.12.0",
"react-redux": "^9.0.4",
"react-router-dom": "^6.21.1",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
}
Example: Create the folder structure and insert the following files accordingly.
// Wishlist/index.jsx import { useSelector } from "react-redux"
import RenderwishlistItems from "./RenderWishlistItems"
export default function Wishlist() {
const {total, totalItems} =
useSelector((state) => state.wishlist)
return (
<div >
<h1>Your wishlist</h1>
<p >{totalItems} Items in wishlist</p>
{
total>0
? (
<div >
<RenderwishlistItems />
</div>
)
: (
<p>Your wishlist is empty</p>
)
}
</div>
)
} |
// Wishlist/RenderWishlistItems.jsx import React from 'react'
import { useDispatch, useSelector } from 'react-redux'
import {RiDeleteBin6Line} from "react-icons/ri"
import { removeFromWishlist } from '../Slices/WishlistSlice' export default function RenderWishlistItems() {
const {wishlist} = useSelector((state) => state.wishlist)
const dispatch = useDispatch();
return (
<div>
{
wishlist.map((dataObj, index) => (
<div
key={index}
className=
{
`flex w-full flex-wrap
items-start justify-between gap-6
${index !== wishlist.length - 1 &&
"border-b border-b-richblack-400 pb-6" }
${index !== 0 && "mt-6" } `
}>
<div>
<div>
{dataObj.title}
</div>
<img src={dataObj.image}
width={200} height={150}
alt= "" />
<p>
{dataObj.description}
</p>
</div>
<div>
<button
onClick={
() =>
dispatch(removeFromWishlist(dataObj._id))
}>
Remove from wishlist
<RiDeleteBin6Line size={20}/>
</button>
</div>
</div>
))
}
</div>
)
} |
// Components/Product_card.jsx import React from 'react'
import { addToWishlist } from '../Slices/WishlistSlice' ;
import { useDispatch } from 'react-redux' ;
import { CiHeart } from "react-icons/ci" ;
const Product_card = ({ dataObj }) => { const dispatch = useDispatch();
const handleAddToWishlist =
() => {
console.log( "dispatching add to Wishlist" )
dispatch(addToWishlist(dataObj));
return ;
}
return (
<div
style={{
display: 'flex' ,
flexWrap: 'wrap' ,
gap: '70px' ,
justifyContent: 'space-around' ,
marginTop: '70px'
}}>
<div
style={{
width: "15em" ,
backgroundColor: "#35D841" ,
padding: 2,
borderRadius: 10,
marginBlock: 10,
}}>
<p style={{ fontSize: 20, color: 'black' }}>
{dataObj.title}
</p>
<img src={dataObj.image} alt= ""
height={200} width={200}
style={{
borderRadius: "15px" ,
}}/>
<p>{dataObj.description}</p>
<CiHeart
onClick={handleAddToWishlist}
size={35} color= 'white' />
</div>
</div>
)
} export default Product_card
|
// Pages/Home.jsx import React, { useEffect, useState } from 'react'
import { FaHeart } from "react-icons/fa" ;
import { Link } from 'react-router-dom' ;
import Product_card from '../Components/Product_card' ;
const items = [ {
"id" : 1,
"title" : "GeeksForGeeks bag" ,
"price" : 109.95,
"description" : "Your perfect pack for everyday use and walks in the forest." ,
"category" : "bag" ,
"image" :
"rating" : {
"rate" : 3.9,
"count" : 120
}
},
{
"id" : 2,
"title" : "GeeksForGeeks tshirt" ,
"price" : 22.3,
"description" : "Slim-fitting style,black tshirt. From geeksforgeeks" ,
"category" : "men's clothing" ,
"image" :
"rating" : {
"rate" : 4.1,
"count" : 259
},
},
{
"id" : 3,
"title" : "GeeksForGeeks bag" ,
"price" : 109.95,
"description" : "Your perfect pack for everyday use and walks in the forest." ,
"category" : "bag" ,
"image" :
"rating" : {
"rate" : 3.9,
"count" : 120
}
},
] const Home = () => { return (
<div>
<Link to={ "/wishlist" }>
<FaHeart size={40} color= "#35D841" />
</Link>
<div
style={{
display: 'Flex ',
justifyContent: ' space-around',
}}>
{items.map((dataObj, index) => {
return (
<Product_card dataObj={dataObj} />
)
})}
</div>
</div>
)
} export default Home
|
// Reducer/index.jsx import { combineReducers } from "@reduxjs/toolkit" ;
import wishlistReducer from "../Slices/WishlistSlice"
const rootReducer = combineReducers({ wishlist: wishlistReducer,
}) export default rootReducer
|
// Slices/WishlistSlice.jsx import { createSlice } from "@reduxjs/toolkit" ;
import { toast } from "react-hot-toast" ;
const initialState = { wishlist: localStorage.getItem( "wishlist" )
? JSON.parse(localStorage.getItem( "wishlist" ))
: [],
total: localStorage.getItem( "total" )
? JSON.parse(localStorage.getItem( "total" ))
: 0,
totalItems: localStorage.getItem( "totalItems" )
? JSON.parse(localStorage.getItem( "totalItems" ))
: 0,
} const wishlistSlice = createSlice({ name: "wishlist" ,
initialState,
reducers: {
addToWishlist: (state, action) => {
const item = action.payload
state.wishlist.push(item)
state.totalItems++
state.total += item.price
localStorage
.setItem( "wishlist" , JSON.stringify(state.wishlist))
localStorage
.setItem( "total" , JSON.stringify(state.total))
localStorage
.setItem( "totalItems" , JSON.stringify(state.totalItems))
toast.success( "Item added to wishlist" )
},
removeFromWishlist: (state, action) => {
const itemId = action.payload
const index =
state.wishlist
.findIndex(
(item) => item._id === itemId)
if (index >= 0) {
state.totalItems--
state.total -= state.wishlist[index].price
state.wishlist.splice(index, 1)
localStorage
.setItem( "wishlist" , JSON.stringify(state.wishlist))
localStorage
.setItem( "total" , JSON.stringify(state.total))
localStorage
.setItem( "totalItems" , JSON.stringify(state.totalItems))
toast
.success( "Item removed from wishlist" )
}
},
}
}) export const { addToWishlist, resetWishlist, removeFromWishlist } = wishlistSlice.actions export default wishlistSlice.reducer;
|
// App.js import './App.css' ;
import { Route, Routes } from "react-router-dom" ;
import Wishlist from "./Wishlist/index"
import Home from "./Pages/Home" ;
function App() {
return (
<div className= "App" >
<Routes>
<Route path= "/wishlist"
element={<Wishlist />} />
<Route path= "/" element={<Home />} />
</Routes>
</div>
);
} export default App;
|
// index.js import React from 'react' ;
import ReactDOM from 'react-dom/client' ;
import './index.css' ;
import App from './App' ;
import reportWebVitals from './reportWebVitals' ;
import rootReducer from './Reducer' ;
import { Provider } from "react-redux" ;
import {configureStore} from "@reduxjs/toolkit"
import { BrowserRouter } from 'react-router-dom' ;
import { Toaster } from 'react-hot-toast' ;
const store = configureStore({ reducer: rootReducer,
}) const root = ReactDOM.createRoot(document.getElementById( 'root' ));
root.render( <React.StrictMode>
<Provider store={store}>
<BrowserRouter>
<App />
<Toaster/>
</BrowserRouter>
</Provider>
</React.StrictMode>
); reportWebVitals(); |
/* App.css */ .App { text-align : center ;
} .App-logo { height : 40 vmin;
pointer-events: none ;
} @media (prefers-reduced-motion: no-preference) { .App-logo {
animation: App-logo-spin infinite 20 s linear;
}
} .App-header { background-color : #282c34 ;
min-height : 100 vh;
display : flex;
flex- direction : column;
align-items: center ;
justify- content : center ;
font-size : calc( 10px + 2 vmin);
color : white ;
} .App-link { color : #61dafb ;
} @keyframes App-logo-spin { from {
transform: rotate( 0 deg);
}
to {
transform: rotate( 360 deg);
}
} |
/* index.css */ body { margin : 0 ;
font-family : -apple-system, BlinkMacSystemFont, 'Segoe UI' , 'Roboto' , 'Oxygen' ,
'Ubuntu' , 'Cantarell' , 'Fira Sans' , 'Droid Sans' , 'Helvetica Neue' ,
sans-serif ;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
} code {
font-family : source-code-pro, Menlo, Monaco, Consolas, 'Courier New' ,
monospace ;
} |
Steps to Run the Application:
npm start
Output: go to this url http://localhost:3000/