I am working on a simple react app. My task is to update the product price as the quantity updates. I have an array of products in my App.js
file.
And also two functions for increment and decrement of the quantity of the product.
import "./App.css";
import Navbar from "./components/navbar";
import ProductList from "./components/ProductList";
import Footer from "./components/Footer";
import React, { useState } from "react";
function App() {
let productList = [
{
price: 99999,
name: "Iphone 10S Max",
quantity: 0,
},
{
price: 999,
name: "Realme 9 Pro",
quantity: 0,
},
];
let [products, setState] = useState(productList);
// console.log(useState(productList));
const increamentQty = (inx) => {
let newProductList = [...products];
newProductList[inx].quantity ;
console.log(newProductList);
setState(newProductList);
};
const decrementQty = (inx) => {
let newProductList = [...products];
newProductList[inx].quantity > 0
? newProductList[inx].quantity--
: (newProductList[inx].quantity = 0);
setState(newProductList);
};
return (
<React.StrictMode>
<Navbar />
<div className="mt-5">
<ProductList
productList={productList}
increamentQty={increamentQty}
decrementQty={decrementQty}
/>
</div>
<Footer />
</React.StrictMode>
);
}
export default App;
I pass the function and the productList array to my ProductList
component.
This is the ProductList.js
file.
import React from "react";
import Product from "./Product";
export default function ProductList(props) {
return props.productList.map((product, i) => {
return (
<Product
product={product}
key={i}
increamentQty={props.increamentQty}
decrementQty={props.decrementQty}
index={i}
></Product>
);
});
}
and then I pass the function and product index to Product component which will render out my products. This is the Product.js
file
import React from "react";
export default function Product(props) {
return (
<div className="container">
<div className="row">
<div className="col-lg-4 col-sm-12 col-md-6">
<h2>
{props.product.name}
<span className="badge bg-success">₹ {props.product.price}</span>
</h2>
</div>
<div className="col-lg-4 col-md-6 col-sm-12">
<div className="btn-group" role="group" aria-label="Basic example">
<button
type="button"
className="btn btn-success"
onClick={() => {
props.increamentQty(props.index);
}}
>
</button>
<button type="button" className="btn btn-warning">
{props.product.quantity}
</button>
<button
type="submit"
className="btn btn-danger"
onClick={() => {
props.decrementQty(props.index);
}}
>
-
</button>
</div>
</div>
<div className="col-lg-4 col-sm-12 col-md-6">
<p>{props.product.quantity * props.product.price}</p>
</div>
</div>
</div>
);
}
As you can see, I am using onClick
event listener for my increase and decrease buttons. When I click the button, the quantity is updated, but that's not reflected in my DOM.
How can I fix this?
CodePudding user response:
In addition to the other suggestions which should fix your issues (ie: using productList={products}
), you're also currently modifying your state directly, which down the line can lead to other UI problems.
When you do:
let newProductList = [...products];
you're creating a shallow copy of the products
array, but the objects within that array are still the original references to your state and are not copies. As a result, you need to create a new object within your new array when you want to update it to avoid rerender issues. One possible way is to use:
const newProductList = [...products];
const toUpdate = newProductList[inx];
newProductList[inx] = {...toUpdate, quantity: toUpdate.quantity 1};
setState(newProductList);
The same applies to your decrement logic. Here I've provided a slightly different example where I'm using the state setter function, but you can also use the above approach. I've created a copy of the state in the arguments and then used Math.max()
instead of the ternary:
setState(([...products]) => {
const toUpdate = newProductList[inx];
products[inx] = {...toUpdate, quantity: Math.max(0, toUpdate.quantity-1)
return products;
});
Always treat your state as read-only/immutable to avoid unexpected bugs with your UI. Other ways of achieving the above is to use .map()
to create a new array and then replace your object when your map reaches the object that matches your inx
value.
CodePudding user response:
In you App.js, change
productList={productList}
to
productList={product}
CodePudding user response:
Just noticed the actual problem. You are passing down the default value for the state and not the state's value.