【问题标题】:cart count is not getting updated properly购物车计数未正确更新
【发布时间】:2021-11-03 16:38:10
【问题描述】:

问题截图:https://streamable.com/ofn42v

它在本地运行良好,但一旦部署到生产(vercel),它就无法运行。我已经尝试了很多不同的事情,比如在购物车中有一个单独的状态,useEffecttotalQuantity 在依赖数组中,但似乎没有任何效果。理想情况下,当上下文中的totalQuantity 更新时,使用它的组件应该按照react doc 中的说明重新渲染,这发生在n2 之间,1 除外。有人可以帮忙吗:(

导航栏中购物车图标的代码:

function Cart(props) {
  const { enableCart, totalQuantity } = useContext(AppContext);

  return (
    <>
      {enableCart ? (
        <Link href="/cart" passHref>
          <a aria-label="Shopping cart" title="Shopping cart">
            <Badge count={totalQuantity} offset={[0, 5]}>
              <ShoppingCartIcon className="w-7 h-7" />
            </Badge>
          </a>
        </Link>
      ) : null}
    </>
  );
}

更新数量-appContext中的代码:

import { useCookies } from "react-cookie";
export const AppProvider = (props) => {

  const [cartItems, updateCart] = useState([]);
  const [totalQuantity, setTotalQuantity] = useState(0);
  const [cookies, setCookie] = useCookies(["cart"]);
  const cookieCart = cookies.cart;

  useEffect(() => {
    cartOperations();
  }, []);

  const calculateAmountQuantity = (items) => {
    let totalCount = 0;
    let totalPrice = 0;
    items.forEach((item) => {
      totalCount += item.quantity;
      totalPrice += item.price * item.quantity;
      setTotalAmount(totalPrice);
      setTotalQuantity(totalCount);
    });
  };

  const cartOperations = async (items) => {
    if (items !== undefined) {
      updateCart([...items]);
      calculateAmountQuantity(items);
    } else if (cookieCart !== undefined) {
      updateCart([...cookieCart]);
      calculateAmountQuantity(cookieCart);
    } else {
      updateCart([]);
      setTotalAmount(0);
      setTotalQuantity(0);
    }
  };
  
  const addItem = (item) => {
    let items = cartItems;
    let existingItem;
    if (items) existingItem = items.find((i) => i.id === item.id);

    if (!existingItem) {
      items = [
        ...(items || []),
        Object.assign({}, item, {
          quantity: 1,
        }),
      ];
      updateCart([...items]);
      setTotalAmount(totalAmount + item.price * 1);
      setTotalQuantity(totalQuantity + 1);
    } else {
      const index = items.findIndex((i) => i.id === item.id);
      items[index] = Object.assign({}, item, {
        quantity: existingItem.quantity + 1,
      });
      updateCart([...items]);
      setTotalAmount(totalAmount + existingItem.price);
      setTotalQuantity(totalQuantity + 1);
    }
    saveCartToCookie(items);
    saveCartToStrapi(items);
  };

我将购物车内容存储在 cookie 中。

AppContext 的代码是 here in github,完整的 nav bar code

直播网址:https://sunfabb.com

转到产品,将几件商品添加到购物车,然后尝试从购物车页面中逐一删除。 (我也在 prod 中启用了 React Profiler)

编辑:这个问题完全是 antd 库特有的。我能够根据以下 2 个答案进一步调试,反应上下文或重新渲染没有任何问题。我尝试为购物车使用自定义徽章,它工作得很好。不过要解决 antd 问题。我可以用自定义的,但是antd的徽章加上一些动画效果更好。

【问题讨论】:

  • 我建议您尝试的一个快速方法是改用setTotalQuantity(value =&gt; value - 1)。我的猜测是这是一个陈旧的关闭问题。
  • 感谢您的建议,但@hackape 似乎在这里不起作用。部署了您建议的更改here
  • 此外,陈旧的关闭对我来说是新事物并进行了查找。根据here 的解释,它不应该从 n...2 中删除项目,但这里只有购物车中的最后一项导致问题。单凭这一点,它怎么会变得陈旧?
  • 这只是一个盲目的猜测。您的应用程序非常复杂,此时我无法发现问题。但我可以看出 a) 它容易受到陈旧关闭问题的影响,并且 b) 我不会以这种方式管理 totalQuantity。它可以从cartItems 派生/计算,手动设置它的值没有意义,它增加了不必要的复杂性并可能引入错误。
  • const [totalPrice, totalQuantity] = cartItems.reduce(([price, quantity], item) =&gt; [price + item.price, quantity + item.quantity], [0, 0])

标签: javascript reactjs next.js antd react-state-management


【解决方案1】:

通过查看渲染并看到刷新后购物车显示为空,这可能是生命周期问题。

我建议创建另一个 useEffect 钩子来监听 totalQuantitytotalAmount (从逻辑上讲,两者中较大的一个,尽管从状态值来看应该没问题)并在钩子调用中更改购物车基于更新总和的图标

编辑:

误读了您的组件间导入,因为购物车(来自 components/index/nav.js)应该监听来自 context.provider 的更改,您可以在 Cart 上使用 context.consumertotalQuantity值(不仅仅是从上下文中导入变量,因为它依赖于其他原因的应用程序渲染)

查看consumer docsthis thread中的示例,并查看this GitHub issues page了解其他人的详细旅程,同时更直接地遇到此问题

【讨论】:

  • 我也试过了,但没用。检查this
  • 不是这样,因为实际上什么都不应该改变,所以虚拟 DOM 没有理由改变,因此,在页面本身中触发其他东西,比如重新渲染购物车图标
  • 对不起,我没有得到你。触发页面中的其他内容,是什么意思?我们只能在某些事情发生变化时重新渲染,对吗? (在这种情况下,计数)。你的意思是强制重新渲染购物车图标?
  • 非常感谢您提供的资源。学到了一些东西。我发现问题根本不在于反应。它与 antd 库的徽章组件一起使用。不知道为什么它不起作用
  • 很高兴听到☺️
【解决方案2】:

正如@hackape 所指出的,当将 state 的值设置为依赖于该 state 先前值的值时,您应该将一个函数传递给 setState 而不是一个值。

所以你应该说setTotalQuantity(previousQuantity =&gt; previousQuantity + 1);,而不是setTotalQuantity(totalQuantity + 1);

这是这样做的安全方式,例如,如果我们尝试同时执行两次,它们都会被考虑在内,而不是使用相同的初始 totalQuantity

我会考虑更改的另一件事是,您在多个位置设置这些数量和数量,并依赖于以前的值。因此,如果它不同步一次,它在下一个操作时也会不同步,依此类推。

您可以为此使用useEffect 挂钩。每次 cartItems 发生变化时,重新计算这些值,并且仅基于新的 cartItems 数组,而不是旧值。

例如这样的:

useEffect(() => {
  setTotalAmount(cartItems.reduce((total, currentItem) => total + (currentItem.price * currentItem.quantity), 0));
  setTotalQuantity(cartItems.reduce((total, currentItem) => total + currentItem.quantity, 0));
}, [cartItems]);

或者,如果您喜欢像现在这样调用它,我仍然会用示例中的 reduce 替换该值,因此它是根据整个购物车而不是之前的值计算的。

购物车通常包含少于 100 个条目,因此无需担心性能。

【讨论】:

  • 关于在 useEffect 中使用 reduce,它是一个不错且干净的解决方案,但它会导致无限循环的重新渲染。 screenshot。正如answer 中所述,似乎在 useEffect 中使用 map/reduce 会重置状态,建议将其移到 useEffect 之外。
猜你喜欢
  • 2019-12-25
  • 2021-12-18
  • 2016-03-30
  • 2018-02-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-05
  • 2014-08-29
相关资源
最近更新 更多