I am working on an e commerce store and I have set up a product page with unique id's and am looping through the items to display them but on the check out page when I try to remove an item I suddenly end up with a nan value. This is the page where I am pulling the items from as a test.
This is the product page that populates the items in the cart, but I am getting a unique key error when I remove an item from the cart if there are 2 or more items in the cart. below is the cart page that shows items in the cart
I have given the key the unique identifier of product but now I am getting a new error of "Warning: Each child in a list should have a unique "key" prop. which results in a nan in the cart when I remove an item. " I am a bit stuck here.
import * as React from 'react';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import Typography from '@mui/material/Typography';
import Modal from '@mui/material/Modal';
import products from './product'
import { useContext } from 'react';
import { useState, useEffect} from 'react';
import cart from '../context/cart'
import { CartContext } from '../context/cart';
const style = {
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: "100%",
bgcolor: 'background.paper',
border: '2px solid #000',
boxShadow: 24,
p: 4,
};
export default function BasicModal() {
const [open, setOpen] = React.useState(false);
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
const { cartItems, addToCart, removeFromCart, clearCart, getCartTotal } = useContext(CartContext)
useEffect(() => {
// storing input name
localStorage.getItem("cartItems", JSON.stringify(cartItems));
}, [cartItems])
// payment is processed here
return (
<div>
<Button onClick={handleOpen} >
({cartItems.length})
Check Out</Button>
<Modal
disableScrollLock={true}
fullScreen={true}
open={open}
onClose={handleClose}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<Box sx={style}>
<>
<div className="flex-col flex items-center bg-white gap-8 p-10 text-black text-sm">
<h1 className="text-2xl font-bold">Cart</h1>
<div className="flex flex-col gap-4">
{cartItems.map((item) => (
<div className="flex justify-between items-center" key={products.id}>
<div className="flex gap-4">
<img src={item.imageId} key="2014" alt={item.title} className="rounded-md h-24" />
<div className="flex flex-col">
<h1 className="text-lg font-bold" key="2015">{item.title}</h1>
<p className="text-gray-600" key="2013">{item.price}</p>
<p className="text-gray-600" key="202">{item.description}</p>
</div>
</div>
<div className="flex gap-4">
<button
className="px-4 py-2 bg-gray-800 text-white text-xs font-bold uppercase rounded hover:bg-gray-700 focus:outline-none focus:bg-gray-700"
onClick={() => {
addToCart(item)
}}
>
+
</button>
<p>{item.quantity}</p>
<button
className="px-4 py-2 bg-gray-800 text-white text-xs font-bold uppercase rounded hover:bg-gray-700 focus:outline-none focus:bg-gray-700"
onClick={() => {
removeFromCart(item)
}}
>
-
</button>
</div>
</div>
))}
</div>
{
cartItems.length > 0 ? (
<div className="flex flex-col justify-between items-center">
<h1 className="text-lg font-bold">Total: ${getCartTotal()}</h1>
<button
className="px-4 py-2 bg-gray-800 text-white text-xs font-bold uppercase rounded hover:bg-gray-700 focus:outline-none focus:bg-gray-700"
onClick={() => {
clearCart()
}}
>
Clear cart
</button>
{/* check out here */}
{/* <Button variant="primary" onClick={} >Check out
</Button> */}
</div>
) : (
<>
<h1 className="text-lg font-bold">Your cart is empty</h1>
</>
)
}
</div>
<div class="col d-flex justify-content-center">
</div>
</>
</Box>
</Modal>
</div>
);
}
this is where my context is
import { createContext, useState, useEffect } from 'react'
import React from 'react'
import products from '../components/product';
export const CartContext = createContext()
export const CartProvider = ({ children }) => {
const [cartItems, setCartItems] = useState(localStorage.getItem('cartItems') ? JSON.parse(localStorage.getItem('cartItems')) : [0])
const addToCart = (item) => {
const isItemInCart = cartItems.find((cartItem) => cartItem.id === item.id);
if (isItemInCart) {
setCartItems(
cartItems.map((cartItem) =>
cartItem.id === item.id
? { ...cartItem, quantity: cartItem.quantity + 1 }
: cartItem
)
);
} else {
setCartItems([...cartItems, { ...item, quantity: 1 }]);
}
};
const removeFromCart = (item) => {
const isItemInCart = cartItems.find((cartItem) => cartItem.id === item.id);
if (isItemInCart && isItemInCart.quantity === 1) {
setCartItems(cartItems.filter((cartItem) => cartItem.id !== item.id));
} else {
setCartItems(
cartItems.map((cartItem) =>
cartItem.id === item.id
? { ...cartItem, quantity: cartItem.quantity - 1 }
: 0,
)
);
}
};
const clearCart = () => {
setCartItems([]);
};
const getCartTotal = () => {
// cart item total is being added here
return cartItems.reduce((total, cartItem) => total + cartItem.price * cartItem.quantity, 0);
};
useEffect(() => {
localStorage.setItem("cartItems", JSON.stringify(cartItems));
}, [cartItems]);
useEffect(() => {
const cartItems = localStorage.getItem("cartItems");
if (cartItems) {
setCartItems(JSON.parse(cartItems));
}
}, []);
const initialValue = 0;
const total = cartItems.reduce((accumulator,current) => accumulator + current.price * current.quantity, initialValue)
return (
<CartContext.Provider
value={{
cartItems,
addToCart,
removeFromCart,
clearCart,
getCartTotal,
}}
>
{children}
</CartContext.Provider>
);
};
How can I resolve this.
I am getting is a nan error whenever I remove an item from a cart if there are more than 2 items in the cart. Unsure of how to resolve this.
{cartItems.map((item) => (
<div className="flex justify-between items-center" key={products.id}>
<div className="flex gap-4">
<img src={item.imageId} key="2014" alt={item.title} className="rounded-md h-24" />
<div className="flex flex-col">
<h1 className="text-lg font-bold" key="2015">{item.title}</h1>
<p className="text-gray-600" key="2013">{item.price}</p>
<p className="text-gray-600" key="202">{item.description}</p>
</div>
as of right now this is my current error index.js:1210 Warning: Each child in a list should have a unique "key" prop.
Check the render method of BasicModal
.
I was able to finally resolve this issue by adding the a unique key to each item in the map.
Here is the context for the cart.
import { createContext, useState, useEffect } from 'react'
import React from 'react';
export const CartContext = createContext()
export const CartProvider = ({ children }) => {
const [cartItems, setCartItems] = useState(localStorage.getItem('cartItems') ? JSON.parse(localStorage.getItem('cartItems')) : [])
const addToCart = (item) => {
const isItemInCart = cartItems.find((cartItem) => cartItem.id === item.id);
if (isItemInCart) {
setCartItems(
cartItems.map((cartItem) =>
cartItem.id === item.id
? { ...cartItem, quantity: cartItem.quantity + 1 }
: cartItem
)
);
} else {
setCartItems([...cartItems, { ...item, quantity: 1 }]);
}
};
const removeFromCart = (item) => {
const isItemInCart = cartItems.find((cartItem) => cartItem.id === item.id);
if (isItemInCart.quantity === 1) {
setCartItems(cartItems.filter((cartItem) => cartItem.id !== item.id));
} else {
setCartItems(
cartItems.map((cartItem) =>
cartItem.id === item.id
? { ...cartItem, quantity: cartItem.quantity - 1 }
: cartItem
)
);
}
};
const clearCart = () => {
setCartItems([]);
};
const getCartTotal = () => {
return cartItems.reduce((total, item) => total + item.price * item.quantity, 0);
};
useEffect(() => {
localStorage.setItem("cartItems", JSON.stringify(cartItems));
}, [cartItems]);
useEffect(() => {
const cartItems = localStorage.getItem("cartItems");
if (cartItems) {
setCartItems(JSON.parse(cartItems));
}
}, []);
return (
<CartContext.Provider
value={{
cartItems,
addToCart,
removeFromCart,
clearCart,
getCartTotal,
}}
>
{children}
</CartContext.Provider>
);
};
and here is the updated code for the check out cart
import * as React from 'react';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import axios from 'axios';
import Button from '@mui/material/Button';
import Dialog from '@mui/material/Dialog';
import DialogActions from '@mui/material/DialogActions';
import DialogContent from '@mui/material/DialogContent';
import DialogContentText from '@mui/material/DialogContentText';
import DialogTitle from '@mui/material/DialogTitle';
import { CartContext } from '../context/cart';
import Axios from 'axios';
import { useContext } from 'react';
import { useState, useEffect} from 'react';
export default function AlertDialog() {
const [open, setOpen] = React.useState(false);
const { cartItems, addToCart, removeFromCart, clearCart, getCartTotal } = useContext(CartContext)
useEffect(() => {
// storing input name
localStorage.getItem("cartItems", JSON.stringify(cartItems));
}, [cartItems])
const handleCheckout = () => {
;
};
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
const style = {
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: "100%",
bgcolor: 'background.paper',
border: '2px solid #000',
boxShadow: 24,
p: 4,
};
return (
<React.Fragment>
<Button variant="outlined" onClick={handleClickOpen}>
Confirm check out here
</Button>
<Dialog
open={open}
disableScrollLock={false}
fullScreen={true}
onClose={handleClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">
{"Cofirm check out here?"}
</DialogTitle>
<DialogContent>
<Box sx={style}>
<>
<div className="flex-col flex items-center bg-white gap-8 p-10 text-black text-sm">
<h1 className="text-2xl font-bold">Cart</h1>
<div className="flex flex-col gap-4">
{cartItems.map((item) => (
<div className="flex justify-between items-center" key={item.id} id={item.id}>
<div className="flex gap-4">
<img src={item.imageId} key={item.imageId } alt={item.title} className="rounded-md h-24" />
<div className="flex flex-col">
<h1 className="text-lg font-bold" key={item.title } >{item.title} </h1>
<p className="text-gray-600" key={item.price } > {item.price}</p>
<p className="text-gray-600"key={item.description }>{item.description} </p>
</div>
</div>
<div className="flex gap-4">
<button
className="px-4 py-2 bg-gray-800 text-white text-xs font-bold uppercase rounded hover:bg-gray-700 focus:outline-none focus:bg-gray-700"
onClick={() => {
addToCart(item)
}}
>
+
</button>
<p >{item.quantity}</p>
<button
className="px-4 py-2 bg-gray-800 text-white text-xs font-bold uppercase rounded hover:bg-gray-700 focus:outline-none focus:bg-gray-700"
onClick={() => {
removeFromCart(item)
}}
>
-
</button>
</div>
</div>
))}
</div>
{
cartItems.length > 0 ? (
<div className="flex flex-col justify-between items-center">
<h1 className="text-lg font-bold">Total: ${getCartTotal()}</h1>
<button
className="px-4 py-2 bg-gray-800 text-white text-xs font-bold uppercase rounded hover:bg-gray-700 focus:outline-none focus:bg-gray-700"
onClick={() => {
clearCart()
}}
>
Clear cart
</button>
{/* check out here */}
{/* check out here */}
{/* <Button variant="primary" onClick={} >Check out
</Button> */}
</div>
) : (
<>
<h1 className="text-lg font-bold">Your cart is empty</h1>
</>
)
}
</div>
<div class="col d-flex justify-content-center">
</div>
</>
</Box>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Close Check Out</Button>
<Button onClick={handleCheckout} autoFocus>
Check out and pay
</Button>
</DialogActions>
</Dialog>
</React.Fragment>
);
}