array in a react component is getting converted into a number(which is of length of that array) after pushing an object into it



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

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.



Source: stackoverflow