React:setState不会重新渲染组件。

3

我正在为电子商务网站实现一个购物车。购物车是一个状态变量,名称为shopCart,用对象数组表示。每个对象包含有关产品的信息,例如标题和价格。我试图实现一个删除按钮,它可以从shopCart状态中删除商品,但更改不会在屏幕渲染时呈现出来。我可以清空购物车,但屏幕仍然显示产品。以下是购物车页面的主要代码:

return (
            <div class={styles.container}>
                <h1>Product</h1><h1>Quantity</h1><h1>Unit price</h1><h1>Total price</h1><div></div>
                {
                    shopCart.map((product, i, array)   => <CartItem key={product.id} product={product} index={i} array={array}/>)
                }
            </div>
        )

以下是CartItem.js的实现代码:

const CartItem = (props) => {
    let { shopCart, setShopCart } = useContext(Context);
    let product = props.product;

// takes the identification of a shopping cart product and removes it from the cart
    const decrease = (element) => {
        shopCart.forEach((el, i) => {
            if (el.hasOwnProperty('id')) {
                if (el.id === element) {
                    let aux = shopCart;
                    aux.splice(i, 1);
                    setShopCart(aux);
                }
            }
        })
    }

    return (
        <div>
            <img src={product.image}></img>
            <h1>{product.quantity}</h1>
            <h1>{product.price}</h1>
            <h1>{product.price * product.quantity}</h1>
            <button onClick={() => {
                decrease(product.id);
            }}>Remove</button>
        </div>
    )
}


为什么即使在每次单击“移除”按钮后,购物车项目被删除了,但购物车仍然无法正确呈现?

1
这个回答解决了你的问题吗?从 React 的状态数组中删除项目 - Emile Bergeron
3个回答

7

问题

您正在修改状态。您保存了对状态的引用,进行了更改,然后将其保存回状态中,因此数组引用永远不会更改。React 在检查状态或道具更新时使用浅相等性。

const decrease = (element) => {
    shopCart.forEach((el, i) => {
        if (el.hasOwnProperty('id')) {
            if (el.id === element) {
                let aux = shopCart; // <-- Saved state ref
                aux.splice(i, 1);   // <-- mutation
                setShopCart(aux);   // <-- Saved ref back to state
            }
        }
    })
}

解决方案

在 React 状态中更新数组的正确方法是将数组元素复制到一个新的数组引用中。这可以通过按项目 ID 过滤当前购物车来轻松完成。我建议更改参数名称,以便更清楚地表示其代表的含义。

const decrease = (id) => {
  setShopCart(shopCart => shopCart.filter(item => item.id !== id));
}

2

您正在直接修改购物车(aux是一个引用),这既是您正在迭代的上下文,也是集合。 您需要确保更新购物车的副本并重置上下文。 最少,您可以执行以下操作:

    const decrease = (element) => {
        shopCart.forEach((el, i) => {
            if (el.hasOwnProperty('id')) {
                if (el.id === element) {
                    let aux = shopCart.slice(); // makes a copy
                    aux.splice(i, 1);
                    setShopCart(aux);
                }
            }
        })
    }

然而,我建议使用 Drew 推荐的方法。当前的方法并不理想。

1
解决方案比您想象的要简单得多。您可以使用array.filter按id删除匹配的产品。
const CartItem = (props) => {
  const { product} = props;
  let { shopCart, setShopCart } = useContext(Context);

  // takes the identification of a shopping cart product and removes it from the cart
  const handleClick = (e) => {
    const filteredShopCart = shopCart.filter(item => item.id !== product.id);
    setShopCart(filteredShopCart);
  };

  return (
    <div>
      <img src={product.image}></img>
      <h1>{product.quantity}</h1>
      <h1>{product.price}</h1>
      <h1>{product.price * product.quantity}</h1>
      <button onClick={handleClick}>Remove</button>
    </div>
  );
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接