在 React 循环内多次调用 setter 时状态未正确更新

2023-11-30

我想一键将多个对象发送到购物车。为此,我循环一个数组并调用addProductToCart但它只保存最后一个对象CartContext state.

当逐个发送对象时(来自不同页面,或者如果发送的是同一页面的一些对象,但不是全部),它会保存在状态CartContext.

当我测试这个时,我在循环时设置了延迟addProductToCart并看到每个物体都在CartContext状态,但下一个对象会覆盖前一个对象。

import React from "react";
import { useContext } from "react";
import { CartContext } from "../../contexts/cart.context";
import Button, { BUTTON_TYPE_CLASSES } from "../buttons/buttons.component";
import {
    BuyButtonCartSymbol,
    BuyButtonCartTitle,
    CurrentPrice,
    CurrentPriceCont,
    CurrentPriceSymbol,
    OldPrice,
    OldPriceCont,
    OldPriceSymbol,
    PriceDetailCont,
    QuantityOldPrice,
    Quantity,
} from "./priceDetail.style";

const PriceDetail = (...props) => {
    const [object] = props;
    const { addItemToCart } = useContext(CartContext);

    const handleOptionsFocus = () => {
        object.setFocusClicked(true);
    };

    let oldPriceSum = 0;
    let newPriceSum = 0;

    // Sum old prices
    for (const value of object.arrayOfObjectsSelected) {
        if (value.oldPrice) {
            oldPriceSum += value.oldPrice;
        } else {
            oldPriceSum += value.price;
        }
    }

    // Sum current prices
    for (const value of object.arrayOfObjectsSelected) {
        newPriceSum += value.price;
    }

    //Multiple objects sending to cart
    var testArr = object.arrayOfObjectsSelected;

    const addProductToCart = () => {
        testArr.forEach((prod, ind) => {
            console.log(ind, prod);
            addItemToCart(prod);
        });
    };

    return (
        <PriceDetailCont>
            <QuantityOldPrice>
                {oldPriceSum !== newPriceSum && (
                    <OldPriceCont>
                        <OldPrice>{oldPriceSum.toLocaleString("fr-FR")}</OldPrice>
                        <OldPriceSymbol> &#8381;</OldPriceSymbol>
                    </OldPriceCont>
                )}
                {object.selectedOptions.length > 1 && (
                    <Quantity onClick={handleOptionsFocus}>
                        [ {object.selectedOptions.length}{" "}
                        {object.selectedOptions.length < 5 ? "pred" : "predov"} ]
                    </Quantity>
                )}
            </QuantityOldPrice>
            <CurrentPriceCont>
                <CurrentPrice>{newPriceSum.toLocaleString("fr-FR")}</CurrentPrice>
                <CurrentPriceSymbol> &#8381;</CurrentPriceSymbol>
            </CurrentPriceCont>
            <Button
                buttonType={BUTTON_TYPE_CLASSES.buy}
                type="button"
                onClick={addProductToCart}
            >
                <BuyButtonCartSymbol />
                <BuyButtonCartTitle>to Cart</BuyButtonCartTitle>
            </Button>
            <Button buttonType={BUTTON_TYPE_CLASSES.oneClick} type="button">
                Instant buy
            </Button>
        </PriceDetailCont>
    );
};

export default PriceDetail;
import { createContext, useState } from "react";

// Helper function to add item to cart with conditions
const addCartItem = (cartItems, productToAdd) => {
    const existingCartItem = cartItems.find(
        (cartItem) => cartItem.pid === productToAdd.pid
    );

    if (existingCartItem) {
        return cartItems.map((cartItem) =>
            cartItem.pid === productToAdd.pid
                ? { ...cartItem, quantity: cartItem.quantity + 1 }
                : cartItem
        );
    }

    return [...cartItems, { ...productToAdd, quantity: 1 }];
};

export const CartContext = createContext({
    cartItems: [],
    addItemToCart: () => {},
});

export const CartProvider = ({ children }) => {
    const [cartItems, setCartItems] = useState([]);

    const addItemToCart = (productToAdd) => {

        setCartItems(addCartItem(cartItems, productToAdd));
    };

    const value = { cartItems, addItemToCart };

    return <CartContext.Provider value={value}>{children}</CartContext.Provider>;
};

Issue

当循环数组并为每次迭代调用状态设置器时,或者当您只是连续多次调用它时,React 将使用所谓的自动配料,并将更改分组到一个渲染中。

为了说明这一点,我创建了下面的工作示例。我们期望有state等于3单击按钮后,但它会1因为它只使用最后一个setState(state + 1).

function App() {
  const [state, setState] = React.useState(0);
  return (
    <div>
      <button
        onClick={() => {
          setState(state + 1);
          setState(state + 1);
          setState(state + 1);
        }}
      >
        Update
      </button>
      state : {state}
    </div>
  );
}

const root = ReactDOM.createRoot(document.getElementById("root"));

root.render(<App />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="root"></div>

Solution

To have 自动配料并且仍然是预期的行为,您可以使用功能状态更新器,像这样:

function App() {
  const [state, setState] = React.useState(0);
  return (
    <div>
      <button
        onClick={() => {
          setState(prev => prev + 1);
          setState(prev => prev + 1);
          setState(prev => prev + 1);
        }}
      >
        Update
      </button>
      state : {state}
    </div>
  );
}

const root = ReactDOM.createRoot(document.getElementById("root"));

root.render(<App />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="root"></div>

对于您的情况,要具有与上述相同的行为,请更改您的调用方式setCartItems in addItemToCart在你的背景下。就像这样:

setCartItems(prevCartItems => addCartItem(prevCartItems, productToAdd));
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 React 循环内多次调用 setter 时状态未正确更新 的相关文章

随机推荐

  • Javafx:更改 setOnAction 中的场景

    我正在构建一个具有多个场景的 JavaFX 应用程序 在 setOnAction 事件中更改场景时 我遇到变量范围问题 这是我的代码 Stage myStage public Scene logInScene all the buttons
  • 如何在 Picturebox 上获得滚动条

    I have PictureBox picture I use picture Size bmp Size picture Image bmp 假设有两个整数maxWidth and maxHeigth 我想添加垂直 水平滚动条pictur
  • 我们可以在 Scala 中进行条件导入吗?

    假设我有以下代码 package my class Foo class Bar extends Foo object Chooser val isFoo true 我导入Foo as import my Foo gt MyClass 我希望
  • UITableView 单元格的动态高度问题 (Swift)

    可变长度的文本数据被注入到表格视图单元格标签中 为了使每个单元格的高度大小合适 我已经实现了viewDidLoad self tableView estimatedRowHeight 88 0 self tableView rowHeigh
  • 通过两个属性就地对列表进行排序

    我有一个具有两个属性的类 名称和位置 我想按位置对此类的列表进行排序 并且具有相同位置的元素应按名称排序 我正在处理静态列表 所以我想就地工作 到目前为止 我设法按一个属性对列表进行排序 list Sort x y gt x Positio
  • 属性“fixed-rate”不允许出现在元素“int:poller”中

    我无法在 poller 标签下添加属性 fixed rate 给出 属性 fixed rate 不允许出现在元素 int poller 中 请参考下面的 xml 文件
  • 在实模式下跳转到远地址

    我遇到一种情况 我必须在实模式下跳转到远地址 我的段值在fs寄存器和偏移量gs寄存器 在跳转过程中我必须保持准确的寄存器内容 我想出了一个想法如下 mov bp fs shl ebp 16 mov bp gs jmp ebp 假设bp fs
  • 在 Google 地图中查找多个位置的中心

    我刚刚复制了这个代码question并应用了我的纬度和经度 然而 纬度和经度将是动态的 并且center地图的内容将根据地点的纬度和经度而变化 以下是另一个问题的代码
  • 如何将 Jersey REST API 转换为可执行 JAR?

    我正在使用 Jersey Maven 并且可以使用 Jetty Tomcat 或 J2EE Preview 可以嵌入吗 将 REST API 移植为独立 可执行 JAR 的最简单方法是什么 没有 Spring Boot 可以吗 按照以下步骤
  • 安装和卸载硬盘

    如何在 Java 编程语言中挂载和卸载硬盘驱动器 与平台无关 因此不使用运行时执行硬编码命令 答案是 是和否 您无法在 java 中挂载或卸载设备 因为每个操作系统都有自己的方法来执行此操作 但是 您可以提供使用适配器模式作为本机接口的ja
  • 为什么around_filter或after_filter中的redirect_to不起作用?

    如何使redirect to在这些过滤器中起作用 我正在努力改变 def start redirect to index end def end redirect to index end to around filter around d
  • 增强的 for 循环不适用于循环体内的 Scanner

    为什么认为行不通 它只打印零 然而 当我使用带有索引值 i 的普通 for 循环并在循环体内使用 a i 时 它会起作用 问题不在于打印循环 因为它不打印值 即使使用正常的 for 循环也是如此 import java util Scann
  • ASM 库计算出错误的堆栈大小

    我使用 ASM 库生成字节码 方法的 最大堆栈大小 将自动计算 在运行时 我发现这个值 最大堆栈大小 不正确 我的源代码是 ClassWriter cw new ClassWriter ClassWriter COMPUTE MAXS Me
  • 将枚举映射到类型

    我正在尝试使用映射类型在枚举上 export enum CurrencyType USD USD AUD AUD GBP GBP CAD CAD 实现这种类型 type Rates AUD number CAD number GBP num
  • Twisted:禁用 Twisted 框架类的日志记录

    我的基于 Twisted 的客户端循环发送 UDP 数据包 因此我使用 DatagramProtocol 类 这是来源 usr bin python coding utf 8 from twisted application service
  • 为什么临时对象被销毁后不崩溃

    class C public int True int i const return i 2 const C F const C c return c int main const C c F C Line 1 cout lt lt boo
  • UNION 到 JPA 查询

    是否可以在 JPA 甚至 Criteria Builder 中查询 UNION 我正在寻找例子 但到目前为止我还没有结果 有人有如何使用它的示例吗 或者说是用原生sql吗 SQL 支持 UNION 但 JPA 2 0 JPQL 不支持 大多
  • 在矩阵中按元素复制[重复]

    这个问题在这里已经有答案了 比方说 我有 A 1 2 3 4 我想使用返回的repmat B 1 1 2 2 1 1 2 2 3 3 4 4 3 3 4 4 恳请您的帮助 谢谢 我不知道使用的方法repmat但这里有一个方法使用kron k
  • jwt令牌过期时间(asp.net core)

    我想增加 JWT 令牌的生命周期 但我做不到 我尝试用谷歌搜索这个问题并找到了参考文献JwtBearerOptions TokenValidationParameters ClockSkew 我还尝试提供 1 分 20 秒的时间跨度 但应用
  • 在 React 循环内多次调用 setter 时状态未正确更新

    我想一键将多个对象发送到购物车 为此 我循环一个数组并调用addProductToCart但它只保存最后一个对象CartContext state 当逐个发送对象时 来自不同页面 或者如果发送的是同一页面的一些对象 但不是全部 它会保存在状