this is the code of App.js
import React,{useState,useRef,useEffect,createContext,useReducer} from 'react' import "./App.css" import NavBar from "./components/NavBar" import Main from "./components/Main" import Modal from "./components/Modal" const Cart_ctx = createContext() const App = () => { const[cartItemCount,setCartItemCount] = useState(0) const[modalOpen,setModalOpen] = useState(false) const[cartItems,setCartItems] = useState([]) return ( <> <Cart_ctx.Provider value={{cartItemCount, setCartItemCount, modalOpen, setModalOpen,cartItems,setCartItems}}> <NavBar/> <Main cartItems={cartItems}/> <Modal modalOpen={modalOpen} cartItems={cartItems}/> </Cart_ctx.Provider > </> ) } export default App export {Cart_ctx}
and this is the code of another component which returns jsx for single food item. i have passed product info object as props and this is the child of Main component
import React,{useContext,useState} from 'react' import {Cart_ctx} from "../App" const Item = ({product}) => { console.log(product); const cart_ctx = useContext(Cart_ctx) const [addItem,setAddItem] = useState(1) const handleclick=()=>{ cart_ctx.setCartItemCount((prev)=>prev+addItem) cart_ctx.setCartItems((prev)=>prev.push(product)) // this line throws error when i clicking the ADD button twice setAddItem(1) console.log(cart_ctx.cartItems); } return ( <> { <div className="item"> <div className="item-info"> <h2>{product.name}</h2> <p >{product.price}</p> <i>{product.discription}</i> </div> <div className="add-cart"> <label htmlFor="qty"> Qty :</label> <input id="qty" type="number" value={addItem} onChange={(e)=>setAddItem(parseInt(e.target.value))} min="1" max="5"/><br/> <button className="button" onClick={handleclick}> ADD </button> </div> </div> } </> ) } export default Item
if i press the ADD button in above component twice ,react throws an error saying .push() is not a function, although it perfectly pushes the product object to the cartItem array the first time. when i log the cartItem value in other component it print the length of the cartItems array.
import React from 'react' const Modal = ({modalOpen,cartItems}) => { console.log(cartItems); //-----> (** this line logs exact length of the array **) if (!modalOpen) return null return ( <div className="overlay"> <div className="cart-modal"> <h1>YOUR CART</h1> {cartItems.map((item)=>{ return( <div>{item.name}</div> ) })} </div> </div> ) } export default Modal
Advertisement
Answer
You are right to suspect there is a problem with this line:
cart_ctx.setCartItems((prev)=>prev.push(product))
The first thing to know is that the array push
function returns the new length of the array. Second, the setCartItems
function will set the state of cart items to be whatever the provided function returns. Since your provided function, (prev)=>prev.push(product)
, returns the result of push
, cart items becomes a number instead of an array.
What you actually want is for the inner function return an array, which you can do by creating a new array:
cart_ctx.setCartItems((prev) => [...prev, product])
I sometimes forget this too about the setWhatever
methods; to help me remember, I mentally think of them as assignment operators. Doing cartItems = cartItems.push(product)
doesn’t make sense, but cartItems = [...cartItems, product]
does.