Mario
Mario

Reputation: 4998

What is the reason for this delay when changing the value of the state variables?

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

Answers (2)

CertainPerformance
CertainPerformance

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

Evan Trimboli
Evan Trimboli

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

Related Questions