\n
Regarding the non-unique keys: you are using a property book.key
which does not seem to exist in your data set. This also makes it impossible to remove an item from the cart or to have multiple different items in the cart since you are matching based on this non-existent key
. The books do have a unique property id
which you should use instead, both as the key
property in your JSX and for filtering the array.
The item.amount++;
in addToCart
is a mutation of state. You need to replace the item
with a copied object.
setCart(\n cart.map((item) =>\n item.id === book.id ? { ...item, amount: item.amount + 1 } : item\n )\n);\n
\ntotal
and cartItems
can be derived from the state of cart
, so they don't really need to be their own state.
Thanks for including a CodeSandbox. It it so much easier to find your problem when I can run your code.
\n","author":{"@type":"Person","name":"Linda Paiste"},"upvoteCount":3}}}Reputation: 472
So I am dealing with rather a complex problem at least for me. Here I have basic cart functionality(add, remove, etc...). The problems I am facing are this. So I am able to add an item to the cart, but when I try to add the different item to the cart it is not doing it. When I try to add the same item that I added before, it is changing the number of cart items, so that is kind of working. I have a problem with a total in the Checkout Component, it is displaying NaN instead of total Price. I know it's is a quite bit of code, and I would be really grateful if someone could point me to my mistakes. Thanks
Context Api
import React from "react";
function getCartFromLocalStorage() {
return localStorage.getItem("cart")
? JSON.parse(localStorage.getItem("cart"))
: [];
}
const CartContext = React.createContext({});
function CartProvider({ children }) {
const [cart, setCart] = React.useState(getCartFromLocalStorage());
const [total, setTotal] = React.useState(0);
const [cartItems, setCartItems] = React.useState(0);
function onUpdate() {
localStorage.setItem("cart", JSON.stringify(cart));
// total
let newTotal = cart.reduce((total, cartItem) => {
return (total += cartItem.amount * cartItem.RegularPrice);
}, 0);
newTotal = parseFloat(newTotal.toFixed(3));
setTotal(newTotal);
// cartItems
let newCartItems = cart.reduce((total, cartItem) => {
return (total += cartItem.amount);
}, 0);
setCartItems(newCartItems);
}
React.useEffect(onUpdate, [cart]);
// remove
const removeItem = (key) => {
setCart([...cart].filter((item) => item.key !== key));
};
// addToCart
const addToCart = (book) => {
const { key, image, bookName, by, RegularPrice } = book;
let item = cart.find((item) => item.key === key);
if (item) {
item.amount++;
onUpdate();
} else {
setCart(
cart.concat({
amount: 1,
price: book.RegularPrice,
...book
})
);
}
};
const clearCart = () => {
setCart([]);
};
return (
<CartContext.Provider
value={{
cart,
cartItems,
total,
removeItem,
addToCart,
clearCart
}}
>
{children}
</CartContext.Provider>
);
}
export { CartContext, CartProvider };
CartLink
import React from "react";
import { Link } from "react-router-dom";
import {FiShoppingCart} from 'react-icons/fi'
import { CartContext } from "../../context/cart";
export default function CartLink() {
const { cartItems} = React.useContext(CartContext);
return (
<div className="cartlink__container">
<Link to="/cart">
<FiShoppingCart />
</Link>
<span className="cartlink__total">{cartItems}</span>
</div>
);
}
Cart Item
import React, { useContext } from "react";
import { CartContext } from "../../context/cart";
import Checkout from "./Checkout";
export default function CartItem({ key, image,bookName, RegularPrice, by, amount }) {
const {removeItem} = useContext(CartContext)
return (
<div key={key} className="cart__item">
<img className='cart__image' src={image} />
<div className='cart__itemdesc'>
<h4>{bookName}</h4>
<h6 className='cart__by'>By: {by}</h6>
<button
className="cart__removebtn"
onClick={() => {
removeItem(key);
}}
>
Remove
</button>
</div>
<span className='circle'><span className='circleone'></span></span>
<span>{RegularPrice}</span>
<div>
<Checkout />
</div>
</div>
);
}
Checkout
import React,{useContext} from 'react'
import { CartContext } from '../../context/cart'
import {Link, useHistory } from 'react-router-dom'
import EmptyCart from './EmptyCart';
const Checkout = () => {
const history = useHistory()
const {cart, total} = useContext(CartContext)
if (cart.length === 0) {
return <EmptyCart />;
}
return (
<div>
<Link to='/stripecontainer' className='checkout__btnOne'>Proceed to
Checkout</Link>
<h2>total : ${total}</h2>
</div>
)
}
export default Checkout
import React,{useState, useEffect, useContext} from 'react'
import './Home.css'
import Books from './Books'
import { BookContext } from "../../context/books";
const Home = () => {
const {data, handleSelectCategory, currentSelectedCategory } =useContext(BookContext)
return (
<div className='books__container' >
<h1 className='categories'>Categories</h1>
{Object.keys(data).map((key, index)=>{
let books = data[key];
return (
<>
<span key={key} onClick={() => handleSelectCategory(key)} className='books__list' >
{books[0].category}
</span>
</>
);})}
<Books category={currentSelectedCategory} />
</div>
)
}
export default Home
Upvotes: 0
Views: 1160
Reputation: 42288
You have two problems in the computation of newTotal
.
The price
for the each of the items in the cart
is a string
like "$17.47"
. Doing math operations on that string
returns NaN
. You should really store a number
here. As a workaround you can use parseFloat
, but you need to remove the $
first because parseFloat("$17.47")
is NaN
.
You are simultaneously assigning and returning. You just want to return
the new value. So don't use +=
.
const newTotal = cart.reduce((total, cartItem) => {
return total + cartItem.amount * parseFloat(cartItem.price.replace('$', ''));
}, 0);
setTotal(newTotal);
Regarding the non-unique keys: you are using a property book.key
which does not seem to exist in your data set. This also makes it impossible to remove an item from the cart or to have multiple different items in the cart since you are matching based on this non-existent key
. The books do have a unique property id
which you should use instead, both as the key
property in your JSX and for filtering the array.
The item.amount++;
in addToCart
is a mutation of state. You need to replace the item
with a copied object.
setCart(
cart.map((item) =>
item.id === book.id ? { ...item, amount: item.amount + 1 } : item
)
);
total
and cartItems
can be derived from the state of cart
, so they don't really need to be their own state.
Thanks for including a CodeSandbox. It it so much easier to find your problem when I can run your code.
Upvotes: 3
Reputation: 6052
I think the NaN
is coming because the RegularPrice
property is moved to price
in addToCart
but then RegularPrice
is used in the reducing function in onUpdate
.
Upvotes: 0
Reputation: 95
I'm unfamiliar with JSon, but if you're getting NaN at the end then try a simple test of printing all the values to the screen to trace where the issue begins (where an unexpected value pops up).
As for the cart item, you say you can only add the same item to the list, then try printing the second item right before adding it to the cart. It could be a reference problem perhaps.
Upvotes: 0