Search code examples
reactjsarray-map

UseState Data is not updating separately for each object


I have made a shopping Cart App, where quantity of each individual items can be increased/Decreased on button click.

I have product data stored in Usestate in App.js file. I am trying to update quantity of each individual items seperately. But the problem is quantity of each items updates with the same value.

Tha flow of quantity data goes upwards, from product.jsx>Card.jsx>App.js datasetter function.

**App.JS code **

function App() {

const [data,setData]=useState([
  {
  id: 1,
  name: "Apple Juice",
  volume: 250,
  inStock: 20,
> quantity: 0,  I am trying to update this value individually.
  price: 2.99,
  image: 'https://media.naheed.pk/catalog/product/cache/ed9f5ebe2a117625f6cd6336daddd764/1/2/1219483-1.jpg',
  },
  {
  id: 2,
  name: "Grapes Juice",
  volume: 500,
  inStock: 10,
  quantity: 0,
  price: 3.19,
  image: 'https://media.naheed.pk/catalog/product/cache/49dcd5d85f0fa4d590e132d0368d8132/1/0/1033730-1.jpg',
  }
  ])

> Here is the setter function Which updates the data.

function datasetter(quantity){
    setData(()=>{
      const updateddata=data.map((data)=>({...data, quantity:quantity}))
    return updateddata;
    
    })
      }
    
     function consoledata(){
    console.log("apple quantity "+data[0].quantity)
    console.log("grape quantity "+data[1].quantity)
      }

  return (

    <div className="bodybg">
     <Card data={data} datasetter={datasetter}/>
     <button onClick={consoledata}>QuantityCheck</button>
                </div>
  );
}

export default App;

Card.jsx code

function Card(props) {
  

>Quantity value passed to app.jsx function

function checkout(quantity){
 props.datasetter(quantity)

}


  return (
    <div> 
        <div className="container p-5">
    <div className="card mt-5">
      <div className="card-header bg-white d-flex">
        <h6 className="flex-grow-1"><b>Shopping Cart</b></h6>
        <a href='https://www.google.com/' className='text-danger text-end'>Remove</a></div>
        <div className="card-body">
{props.data.map(data=>( 
<Product data={data} key={data.id} productquantity={checkout}/>))
}
            </div>
            <div className='card-footer bg-white'>
              <div className="p-2 flex-row ">
              <div className='row h-25'><h6><b>Sub-Total</b></h6></div>
              <div className='row text-secondary'><p>2 Items</p></div>
              <div className='row text-secondary'><b>{"$6.18"}</b></div>
              <div className='row col-3'><button className='btn btn-primary rounded-5'>Checkout</button></div>
              </div>
            </div>
            </div>
            </div>
            </div>
  )
}


export default Card

**Product.JSX code **

function Product({data, productquantity}) {
    const [quantity,setQuantity]= useState(0)

>Quantity value passed to Card.jsx function

useEffect(()=>{
      productquantity(quantity);
    },[quantity])

    let stockChecker="";


if (data.inStock>0) stockChecker="bg-success"

else stockChecker="bg-danger"
   
> Functions for quantity increment/ decrement

    function increment(){
        setQuantity((quantity) => (quantity+1 ));
        

        // setQuantity((quantity)=>setQuantity(quantity+1))
        
        
    }

    function decrement(){
        if (quantity>0)
        setQuantity((quantity) => (quantity-1 ))
      
       
    }

  return (
    <div><div className="d-flex flex-row mb-3">
    <div className="col-2"><img src={data.image} alt="Product_Image" className="productimg"/>
    </div>
    <div className="p-2 flex-col flex-grow-1">
      <div className='row h-25'><h4><b>{data.name}</b></h4></div>
      <div className='row text-secondary h-25'><p>{data.volume} ml</p></div>
      <div className='row col-1 ms-1'><div className={`text-primary ${stockChecker} rounded-5 stockcheck`}></div></div>
      </div>
    <div className="p-2 my-4 mx-5">
      <button className='btn btn-secondary rounded-5 mx-3' onClick={increment}>+</button>
      <b>{quantity}</b>
      <button className='btn btn-secondary rounded-5 mx-3' onClick={decrement}>-</button>
    </div>
    <div className="p-2 flex-col">
      <div className='row'><h4><b>${data.price}</b></h4></div>
      <div className='row text-secondary'><a href='https://www.google.com/' className='text-primary'>Save for later</a></div>
      <div className='row text-secondary h-25'><a href='https://www.google.com/' className='text-danger text-end'>Remove</a></div>
      </div>

    </div>
    </div>
  )
}



export default Product

Quantity property from each object update with same values. If quantity of product 1 is 5, then 2nd product also updates to 5.

[
  {
  id: 1,
  name: "Apple Juice",
  volume: 250,
  inStock: 20,
 **quantity: 5**,
  price: 2.99,
  image: 'https://media.naheed.pk/catalog/product/cache/ed9f5ebe2a117625f6cd6336daddd764/1/2/1219483-1.jpg',
  },
  {
  id: 2,
  name: "Grapes Juice",
  volume: 500,
  inStock: 10,
 ** quantity: 5**,
  price: 3.19,
  image: 'https://media.naheed.pk/catalog/product/cache/49dcd5d85f0fa4d590e132d0368d8132/1/0/1033730-1.jpg',
  }
  ]

What I want is quantity to be updated seprately with different values.


Solution

  • If you just want to be able to update the data in the first object, modifying the setter function as follows does the job:

      function datasetter(quantity) {
        setData(()=>{
          const updateddata = [{...data[0], quantity}, ...data.slice(1)]
          return updateddata;
        })
      }
    

    However, if you want to be able to modify any object in the array, you'll want to add a parameter to your function to specify, say, the index of the object to change. For instance:

      function datasetter(quantity, index) {
        setData(()=>{
          const updateddata = [...data]
          updateddata[index] = {...data[index], quantity}
          return updateddata;
        })
      }