When i add product to cart it goes with quantity=2 and when i go back and again go to my cart page then the quantity become 4 and it increases with 2 always.
Here is my addProduct() function.
import axios from "axios";
import React, { useEffect, useState } from "react";
import { NavLink, useParams } from "react-router-dom";
import { useDispatch } from "react-redux";
import { addToCart } from "../Redux/action/action";
import { useSelector } from "react-redux";
import { FaStar } from "react-icons/fa";
import Skeleton from "react-loading-skeleton";
const ProductDetails = () => {
const { id } = useParams();
const cartItems = useSelector((state) => state.handleCart);
const [isLoading, setIsLoading] = useState(true);
const [product, setProduct] = useState([]);
const dispatch = useDispatch();
useEffect(() => {
const fetchProduct = async () => {
try {
setIsLoading(true);
const { data } = await axios.get(
`https://fakestoreapi.com/products/${id}`
);
setProduct(data);
setIsLoading(false);
} catch (error) {
console.error("Error fetching product:", error);
} finally {
setIsLoading(false);
}
};
fetchProduct();
}, [id]);
const addProduct = (product) => {
if (product) {
dispatch(addToCart(product));
// Retrieve existing cart items from localStorage
const existingCartItemsJSON = localStorage.getItem("CartItems");
const existingCartItems = existingCartItemsJSON
? JSON.parse(existingCartItemsJSON)
: [];
// Ensure that existingCartItems is an array
if (!Array.isArray(existingCartItems)) {
console.error("Invalid existingCartItems:", existingCartItems);
return;
}
// Add the new product to the existing cart items
const updatedCartItems = [...existingCartItems, product];
// Store the updated cart items back in localStorage
localStorage.setItem("CartItems", JSON.stringify(updatedCartItems));
} else {
console.error("Invalid product:", product);
}
};
useEffect(() => {
const cartJSON = localStorage.getItem("CartItems");
if (cartJSON) {
const cartArray = JSON.parse(cartJSON);
// Ensure cartArray is always an array
if (!Array.isArray(cartArray)) {
console.error("Invalid cartArray:", cartArray);
return;
}
//Dispatch addToCart action to populate the Redux store with items from local storage
cartArray.forEach((item) => {
dispatch(addToCart(item));
});
}
}, [dispatch]);
const ShowProducts = () => (
<div className="d-flex row" key={product.id}>
<div className="col-md-6 col-sm-3 mt-5">
<img
src={product.image}
alt={product.title}
height="400px"
width="400px"
/>
</div>
<div className="col-md-6 mt-5">
<h4 className="text-uppercase text-black-50">{product.category}</h4>
<h1 className="display-5">{product.title}</h1>
<p className="lead fw-bolder">
Rating {product.rating && product.rating.rate}
<FaStar />
</p>
<h3 className="display-6 fw-bolder my-4">${product.price}</h3>
<p className="lead">{product.description}</p>
<button className="btn btn-primary" onClick={() => addProduct(product)}>
Add to Cart
</button>
<NavLink to="/MyCart" className="btn btn-outline-dark ms-2">
Go to Cart
</NavLink>
</div>
</div>
);
return (
<>
<div className="container py-5">
<div className="row">
{isLoading ? (
<>
{" "}
<div className="col-md-6">
<Skeleton height={400} />
</div>
<div className="col-md-6">
<Skeleton width={300} height={50} />
<Skeleton height={75} />
<Skeleton width={25} height={150} />
<Skeleton height={50} />
<Skeleton height={150} />
<Skeleton height={50} width={100} />
<Skeleton height={50} width={100} />
</div>
</>
) : (
product && <ShowProducts />
)}
</div>
</div>
</>
);
};
export default ProductDetails;
here i remove data from local storage buy clicking on remove the data remove from local storage but in my cart page it is still there because it has quantity = 2
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { removeFromCart, addToCart } from "../Redux/action/action";
import { NavLink } from "react-router-dom";
const MyCart = () => {
const cartItem = useSelector((state) => state.handleCart);
const dispatch = useDispatch();
useEffect(() => {
const cartJSON = localStorage.getItem("CartItems");
if (cartJSON) {
const cartArray = JSON.parse(cartJSON);
// Ensure cartArray is always an array
if (!Array.isArray(cartArray)) {
console.error("Invalid cartArray:", cartArray);
return;
}
//Dispatch addToCart action to populate the Redux store with items from local storage
cartArray.forEach((item) => {
dispatch(addToCart(item));
});
}
}, [dispatch]);
const handleClose = (item) => {
dispatch(removeFromCart(item));
// Remove the item from local storage as well
const existingCartItemsJSON = localStorage.getItem("CartItems");
const existingCartItems = existingCartItemsJSON
? JSON.parse(existingCartItemsJSON)
: [];
const updatedCartItems = existingCartItems.filter(
(cartItem) => cartItem.id !== item.id
);
localStorage.setItem("CartItems", JSON.stringify(updatedCartItems));
};
const cartItems = (item) => {
return (
<div className="px-4 my-5 bg-light rounded-3" key={item.id}>
<div className="container py-4">
<button
className="btn-close float-end"
aria-label="Close"
onClick={() => handleClose(item)}
></button>
<div className="d-flex row justify-content-center">
<div className="col-md-4">
<img
src={item.image}
alt={item.title}
height="200px"
width="180px"
/>
</div>
<div className="col-md-4 ">
<h3> {item.title} </h3>
<p className="lead"> {item.description} </p>
<p className="lead fw-bolder">${item.price}</p>
<p>{item.qty}</p>
</div>
</div>
</div>
</div>
);
};
const emptyCart = () => {
return (
<div className="my-4 px-3 bg-light rounded-3 ">
<div className="container py-4 ">
<div className="row">
<h3>Your Cart is Empty</h3>
</div>
</div>
</div>
);
};
const checkoutButton = () => {
return (
<div className="container ">
<div className="row">
<NavLink
to="/checkout"
className="btn mb-5 btn-outline-primary w-25 mx-auto"
>
Checkout
</NavLink>
</div>
</div>
);
};
return (
<>
{cartItem.length === 0 && emptyCart()}
{cartItem.length !== 0 && cartItem.map(cartItems)}
{cartItem.length !== 0 && checkoutButton()}
</>
);
};
export default MyCart;
Here is my Reducer.js function. Here i increasing the quantity of an item by 1 if it is already present in the cart in ADD_TO_CART case and decreasing the quantity by 1 when the quantity is greater than 1 in REMOVE_FROM_CART case
import { ADD_TO_CART, REMOVE_FROM_CART } from "../action/action-type";
const cart = [];
const handleCart = (state = cart, action) => {
const product = action.payload;
switch (action.type) {
case ADD_TO_CART:
const existingProduct = state.find((item) => item.id === product.id);
if (existingProduct) {
return state.map((item) =>
item.id === product.id ? { ...item, qty: item.qty + 1 } : item
);
} else {
const product = action.payload;
return [
...state,
{
...product,
qty: 1,
},
];
}
case REMOVE_FROM_CART:
const existingProductToRemove = state.find(
(item) => item.id === product.id
);
if (existingProductToRemove.qty === 1) {
// Remove the item from the state
const newState = state.filter((item) => item.id !== product.id);
// Store the updated cart state in local storage
localStorage.setItem("CartItems", JSON.stringify(newState));
return newState;
} else {
// Decrease the quantity of the item in the state
const newState = state.map((item) =>
item.id === product.id ? { ...item, qty: item.qty - 1 } : item
);
// Store the updated cart state in local storage
localStorage.setItem("CartItems", JSON.stringify(newState));
return newState;
}
default:
return state;
}
};
export default handleCart;
Please provide solution for this
Regarding the conversation in this post, the problem is the useEffect
was running the wrong logic. In the cart element, it should ONLY READ the cartItems
state and display them. Because cart items state mutation should be done through actions like adding or removing, not in a side effect on mount like this.
You should add another action updateCartItemsFromCache()
which takes no params and just overrite the state by the cache and call it in the useEffect
of the Cart component instead.
For this type of cache, I highly recommend you to use redux-persist
. This will write cache functions to make sure your local storage and your states always synced. Although the package is cut off from maintenance, it is still stable enough to use in production. And they also promise to implement a new alternative for it soon.
Hapy coding!