I’m pretty new to React, and I’m making an ecommerce website for practice. I have a cart component that dynamically renders cart items from an API, but I narrowed the problem 100% down to the front end. On the initial render, it works fine, everything that is in the database cart appears, however after I press the “Delete from Cart” button on a cart item, each cart item doubles.
CartCard.jsx (/api/delete returns a json object of the cart AFTER deletion. deletion is handled in the api)
import React from "react"; import CartCardCSS from "./CartCard.module.css"; export default function CartCard(props) { const passCart = props.passCart; function deleteFromCart(e) { e.preventDefault(); const item = { id: props.id, mainText: props.mainText, price: props.price, type: props.type } const user = { username: props.username } const compoundEntity = { theContent: item, theUser: user } const requestOptions = { method: "POST", headers: {"Content-Type" : "application/json"}, body: JSON.stringify(compoundEntity) } fetch("/api/delete", requestOptions) .then(response => response.json()) .then(response => { passCart(response) console.log(response) }) } return ( <div className={CartCardCSS.card}> <img src={props.image} className={CartCardCSS.itempicture} alt="item picture"/> <h3 className={CartCardCSS.subitem}>{props.mainText}</h3> <h4 className={CartCardCSS.subitem}>{props.price}</h4> <button onClick={deleteFromCart} className={`${CartCardCSS.subitem} ${CartCardCSS.button}`} type="button">Remove From Cart</button> </div> ) }
Cart.jsx:
import React, {useEffect, useState} from "react"; import CartCard from "./CartCard"; import CpuPicture from "./img/cpu.jpg"; import GpuPicture from "./img/gpu.jpg"; export default function Cart(props) { const cart = props.cart; const username = props.username; const passCart = props.passCart; const [arrCart, setArrCart] = useState(Object.values(cart)) const [cartComp, setCartComp] = useState() useEffect(() => { console.log(cart) setArrCart(Object.values(cart)) for(let i = 0; i < arrCart.length; i++) { setCartComp(arr => [arr, <CartCard key={i} id={i} passCart={passCart} username={username} mainText={arrCart[i].mainText} price={arrCart[i].price} type={arrCart[i].type} image={arrCart[i].type === "cpu" ? CpuPicture : GpuPicture}/>]) } }, [cart]) return ( <div> <h2 >Cart:</h2> {cartComp} </div> ) }
Profile.jsx:
import React from "react"; import Cart from "./Cart"; import Navbar from "./Navbar"; import {useNavigate} from "react-router-dom"; export default function Profile(props) { const username = props.username; const isLoggedIn = props.isLoggedIn; const cart = props.cart; const passCart = props.passCart; let navigate = useNavigate(); const routeChange = () => { let path = "/login"; navigate(path); } if (isLoggedIn) { return ( <div> <Navbar /> <h1>{username}</h1> <Cart passCart={passCart} username={username} cart={cart} /> </div> ) } else { routeChange(); } }
App.jsx
import React, {useState} from "react"; import './App.css'; import Login from "./components/Login"; import Signup from "./components/Signup"; import Home from "./components/Home"; import Profile from "./components/Profile"; import Card from "./components/Card"; import GpuPicture from "./components/img/gpu.jpg"; import CpuPicture from "./components/img/cpu.jpg"; import {BrowserRouter, Routes, Route, Navigate} from "react-router-dom"; function App() { const [username, setUsername] = useState(''); const [cart, setCart] = useState(''); const [isLoggedIn, setIsLoggedIn] = useState(false) function passUsername(items) { setUsername(items); } function passCart(items) { setCart(items); } function passIsLoggedIn(items) { setIsLoggedIn(items); } return ( <div className="App"> <BrowserRouter> <Routes> <Route path="/" element={<Navigate to="/login"/>} /> <Route path="/login" element={<Login passIsLoggedIn={passIsLoggedIn} passUsername={passUsername} passCart={passCart}/>}/> <Route path="/signup" element={<Signup />}/> <Route path="/home" element={<Home passCart={passCart} cart={cart} username={username} isLoggedIn={isLoggedIn} />} /> <Route path="/profile" element={<Profile passCart={passCart} cart={cart} username={username} isLoggedIn={isLoggedIn}/>} /> </Routes> </BrowserRouter> </div> ); } export default App;
Thanks for your help
Advertisement
Answer
This for
loop might be the issue:
useEffect(() => { console.log(cart) setArrCart(Object.values(cart)) for(let i = 0; i < arrCart.length; i++) { setCartComp(arr => [arr, <CartCard key={i} id={i} passCart={passCart} username={username} mainText={arrCart[i].mainText} price={arrCart[i].price} type={arrCart[i].type} image={arrCart[i].type === "cpu" ? CpuPicture : GpuPicture}/>]) } }, [cart])
Keep in mind that setState
in react is asynchronous. What it means:
console.log(arrCart) // previous state setArrCart(Object.values(cart)) console.log(arrCart) // still previous state
Try changing your for
loop into that:
for(let i = 0; i < cart.length; i++) { setCartComp(arr => [arr, <CartCard key={i} id={i} passCart={passCart} username={username} mainText={cart[i].mainText} price={cart[i].price} type={cart[i].type} image={cart[i].type === "cpu" ? CpuPicture : GpuPicture}/>]) }