Reputation: 4998
Having the following code, I hope that when changing the value of price and quantity the status of products is modified in the parent component, this works partially, because there is a delay in updating the information, I must write twice the value of price and quantity to see some result. What is the reason for this delay when changing the value of the state variables?
import React, { useState } from "react";
const initialState = [
{
id: 1,
name: "Nintendo switch",
price: 250,
quantity: 0,
subTotal: 0
},
{
id: 2,
name: "Nintendo 3DS",
price: 199,
quantity: 0,
subTotal: 0
},
{
id: 3,
name: "playstation 4",
price: 300,
quantity: 0,
subTotal: 0
}
];
function Totals({ products }) {
function getTotal(key) {
return products.reduce((previousValue, currentValue) => {
return (previousValue += currentValue[key]);
}, 0);
}
return (
<tr>
<td />
<td>{getTotal("price")}</td>
<td>{getTotal("quantity")}</td>
<td>{getTotal("subTotal")}</td>
</tr>
);
}
function Product({ product, onChange }) {
const [price, setPrice] = useState(product.price);
const [quantity, setQuantity] = useState(product.quantity);
function onChangePrice(event) {
setPrice(event.target.value);
onChange({ id: product.id, price, quantity });
}
function onChangeQuantity(event) {
setQuantity(event.target.value);
onChange({ id: product.id, price, quantity });
}
return (
<tr>
<td>{product.name}</td>
<td>
<input onChange={onChangePrice} value={price} />
</td>
<td>
<input onChange={onChangeQuantity} value={quantity} />
</td>
<td>{product.subTotal}</td>
</tr>
);
}
function App() {
const [products, setProducts] = useState(initialState);
function onChangeHandler(entry) {
const output = products.map(product =>
product.id === entry.id
? {
...product,
price: +entry.price,
quantity: +entry.quantity,
subTotal: +entry.price * +entry.quantity
}
: product
);
setProducts(output);
}
return (
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
<th>Quantity</th>
<th>Sub total</th>
</tr>
</thead>
<tbody>
{products.map(product => (
<Product
key={product.id}
product={product}
onChange={onChangeHandler}
/>
))}
<Totals products={products} />
</tbody>
</table>
);
}
export default App;
Please take a look at this example
https://codesandbox.io/s/gracious-ride-tixs1
Upvotes: 0
Views: 69
Reputation: 370789
Although the Product
's local price
and quantity
are being set properly, you're calling onChange
, which is the prop passed down by the parent component, with the old values:
function onChangePrice(event) {
setPrice(event.target.value);
onChange({ id: product.id, price, quantity });
}
function onChangeQuantity(event) {
setQuantity(event.target.value);
onChange({ id: product.id, price, quantity });
}
If you really need to store the price and quantity in the child component as well as the parent component for some reason, make sure to call onChange
with the event.target.value
instead of the old values in price
and quantity
:
function onChangePrice(event) {
setPrice(event.target.value);
onChange({ id: product.id, price: event.target.value, quantity });
}
function onChangeQuantity(event) {
setQuantity(event.target.value);
onChange({ id: product.id, price, quantity: event.target.value });
}
Upvotes: 1
Reputation: 30082
The issue is that you're essentially storing the state in 2 places and they don't always agree. The Product
component should be stateless, since you're maintaining the state in your list of products.
function Product({ product, onChange }) {
function onChangePrice(e) {
onChange({ id: product.id, price: e.target.value, quantity: product.quantity });
}
function onChangeQuantity(e) {
onChange({ id: product.id, quantity: e.target.value, price: product.price });
}
return (
<tr>
<td>{product.name}</td>
<td>
<input onChange={onChangePrice} value={product.price} />
</td>
<td>
<input onChange={onChangeQuantity} value={product.quantity} />
</td>
<td>{product.subTotal}</td>
</tr>
);
}
Upvotes: 2