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.
.jpg)
Prerequisites Approach to create Wishlist Functionality:- Install Redux Toolkit:Use npm or yarn to install Redux Toolkit:
npm install @reduxjs/toolkit or yarn 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 like addToWishlist and removeFromWishlist . - 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 and useSelector hooks from react-redux in components.Dispatch actions (e.g., addToWishlist , removeFromWishlist ) based on user interactions.
Steps to Create and Configure React App with redux toolkitStep 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.
CSS
/* App.css */
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
CSS
/* 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;
}
JavaScript
// 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>
)
}
JavaScript
// 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>
)
}
JavaScript
// 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
JavaScript
// 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":
"https://practice.horje.org/_next/image?url=https%3A%2F%2Fmedia.geeksforgeeks.org%2Fimg-practice%2FMaskGroup31-1651641966.png&w=640&q=75",
"rating": {
"rate": 3.9,
"count": 120
}
},
{
"id": 2,
"title": "GeeksForGeeks tshirt",
"price": 22.3,
"description": "Slim-fitting style,black tshirt. From horje",
"category": "men's clothing",
"image":
"https://practice.horje.org/_next/image?url=https%3A%2F%2Fmedia.geeksforgeeks.org%2Fimg-practice%2FGroup7895-1651644285.png&w=640&q=75",
"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":
"https://practice.horje.org/_next/image?url=https%3A%2F%2Fmedia.geeksforgeeks.org%2Fimg-practice%2FMaskGroup31-1651641966.png&w=640&q=75",
"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
JavaScript
// Reducer/index.jsx
import { combineReducers } from "@reduxjs/toolkit";
import wishlistReducer from "../Slices/WishlistSlice"
const rootReducer = combineReducers({
wishlist: wishlistReducer,
})
export default rootReducer
JavaScript
// 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;
JavaScript
// 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;
JavaScript
// 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();
Steps to Run the Application:
npm start Output: go to this url http://localhost:3000/
|