You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在同一组件中多次使用带不同参数的RTK Query函数?

解决RTK Query购物车计数器共享加载状态问题

问题根源

你当前的代码中,所有购物车商品共用同一个useAddCartMutation和useRemoveCartMutation实例,导致它们的isLoading状态全局共享——点击任意一个商品的加减按钮,所有按钮都会显示加载状态。

你之前尝试在map循环内调用mutation hook的做法,违反了React Hooks核心规则:Hooks必须在组件顶层作用域调用,不能在循环、条件或嵌套函数中调用,因此触发了TypeScript错误。

解决方案

通过在父组件维护一个状态,单独跟踪每个商品ID对应的加减操作加载状态,再将对应状态传递给每个QCounter组件,实现单个按钮的加载状态独立控制。

修改Cart组件代码

import { useState } from 'react';
// 导入你的类型定义和RTK Query hooks

const Cart = () => {
  const {
    isLoading: cartLoading,
    isSuccess,
    data: cart,
  } = useGetCartQuery({ userId: userData.id });
  const [addItem] = useAddCartMutation();
  const [removeItem] = useRemoveCartMutation();
  
  // 维护每个商品的加载状态:key为productId,value存add/remove的加载状态
  const [itemLoadingStates, setItemLoadingStates] = useState<Record<string, { add: boolean; remove: boolean }>>({});

  const handleAddBtn = async (item: cartItemType) => {
    if (item.product.availability <= item.quantity) {
      throw new Error("库存不足,无法增加数量");
    }

    const productId = item.product.id as string;
    // 设置当前商品的添加按钮为加载中
    setItemLoadingStates(prev => ({
      ...prev,
      [productId]: { ...prev[productId], add: true }
    }));

    try {
      // 调用mutation并等待请求完成
      await addItem({
        userId: userData.id as string,
        productId,
        quantity: 1,
      }).unwrap();
    } catch (error) {
      console.error("添加商品失败:", error);
    } finally {
      // 请求结束后,取消加载状态
      setItemLoadingStates(prev => ({
        ...prev,
        [productId]: { ...prev[productId], add: false }
      }));
    }
  };

  const handleRemoveBtn = async (item: cartItemType, count: number) => {
    if (item.quantity < count) return;

    const productId = item.product.id as string;
    // 设置当前商品的移除按钮为加载中
    setItemLoadingStates(prev => ({
      ...prev,
      [productId]: { ...prev[productId], remove: true }
    }));

    try {
      await removeItem({
        userId: userData.id as string,
        productId,
        count,
      }).unwrap();
    } catch (error) {
      console.error("移除商品失败:", error);
    } finally {
      setItemLoadingStates(prev => ({
        ...prev,
        [productId]: { ...prev[productId], remove: false }
      }));
    }
  };

  if (cartLoading) return <div>加载购物车中...</div>;
  if (!isSuccess || !cart) return <div>购物车为空</div>;

  return (
    <div>
      {cart.items.map((item: cartItemType) => {
        const productId = item.product.id as string;
        // 获取当前商品的加载状态,默认都为false
        const { add: isAddLoading, remove: isRemoveLoading } = itemLoadingStates[productId] || { add: false, remove: false };
        
        return (
          <QCounter
            key={productId}
            availability={item.product.availability}
            count={item.quantity}
            handleAddBtn={() => handleAddBtn(item)}
            handleRemoveBtn={() => handleRemoveBtn(item, 1)}
            AddBtnLoading={isAddLoading}
            RemoveBtnLoading={isRemoveLoading}
            height="36px"
            width="36px"
          />
        );
      })}
    </div>
  );
};

QCounter组件无需修改

你的QCounter组件逻辑正确,只要接收正确的单个商品加载状态即可正常工作。

原理说明

  1. 通过useState创建对象状态,以商品ID为键,存储每个商品的加减操作加载状态。
  2. 在调用mutation前后,分别更新对应商品的加载状态:请求开始时设为true,请求完成(成功/失败)后设为false。
  3. 渲染每个QCounter时,根据商品ID取出对应的加载状态传递给组件,实现单个按钮的独立加载状态控制。

内容的提问来源于stack exchange,提问作者HUS.97

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 00:27:17