Horje
Wishlist Functionality using Redux Toolkit in React

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.

preview--(1)

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 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:

ssps-

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/




Reffered: https://www.geeksforgeeks.org


ReactJS

Related
Top 10 tricks to make your React App Faster Top 10 tricks to make your React App Faster
Implementing Add to Cart functionality using Redux toolkit in React Implementing Add to Cart functionality using Redux toolkit in React
Difference between useState and useReducer Difference between useState and useReducer
State Management in React – Hooks, Context API and Redux State Management in React – Hooks, Context API and Redux
React Tutorial React Tutorial

Type:
Geek
Category:
Coding
Sub Category:
Tutorial
Uploaded by:
Admin
Views:
14