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

React Native中Redux Toolkit购物车商品数量递增异常问题

问题修复:React Native Redux Toolkit购物车重复添加商品及Key警告

当前问题核心:点击购物车加号按钮时,商品会重复生成条目,同时出现「存在相同key的子元素」警告,根源在于Redux reducer未处理商品数量逻辑,直接追加新条目,且购物车页面的数量显示、增减逻辑存在错误。


1. 重构BasketSlice的Reducer逻辑

修改addToBasket和removeFromBasket,实现"存在则增减数量,不存在则新增/删除"的逻辑:

import { createSlice } from "@reduxjs/toolkit";

const initialState = {
  items: [],
};

export const basketSlice = createSlice({
  name: "basket",
  initialState,
  reducers: {
    addToBasket: (state, action) => {
      // 查找购物车中是否已有该商品
      const existingItem = state.items.find(item => item.id === action.payload.id);
      if (existingItem) {
        // 存在则递增数量
        existingItem.quantity += 1;
      } else {
        // 不存在则新增条目,默认数量为1
        state.items.push({ ...action.payload, quantity: 1 });
      }
    },
    removeFromBasket: (state, action) => {
      const existingItem = state.items.find(item => item.id === action.payload.id);
      if (existingItem) {
        if (existingItem.quantity > 1) {
          // 数量大于1则递减数量
          existingItem.quantity -= 1;
        } else {
          // 数量为1则删除整个条目
          state.items = state.items.filter(item => item.id !== action.payload.id);
        }
      } else {
        console.warn(`无法移除商品(id: ${action.payload.id}),因为它不在购物车中!`);
      }
    },
  },
});

export const { addToBasket, removeFromBasket } = basketSlice.actions;
export const selectBasketItems = (state) => state.basket.items;
export default basketSlice.reducer;

2. 修正CartScreen的错误逻辑

修复数量显示、增减函数及总价计算:

import { SafeAreaView, View, Text, Image, TouchableOpacity } from "react-native";
import React from "react";
import { useDispatch, useSelector } from "react-redux";
import { addToBasket, removeFromBasket, selectBasketItems } from "../../features/basketSlice";
import { AntDesign } from "@expo/vector-icons";

const CartScreen = () => {
  const dispatch = useDispatch();
  const items = useSelector(selectBasketItems);

  const handleDecrement = (id) => {
    dispatch(removeFromBasket({ id }));
  };

  const handleIncrement = (item) => {
    // 直接触发addToBasket,reducer会自动处理数量递增
    dispatch(addToBasket(item));
  };

  // 修正总价计算:单价 × 数量后累加
  const totalPrice = items.reduce((total, item) => total + (item.price * item.quantity), 0);

  return (
    <SafeAreaView className="flex-1 bg-white">
      {items.length === 0 ? (
        <View className="flex-1 items-center justify-center">
          <Text className="text-lg font-semibold">您的购物车是空的</Text>
        </View>
      ) : (
        <View className="flex-1">
          {items.map((item) => (
            <View key={item.id} className="">
              <View className="flex-row my-1 mx-3">
                <View className="flex flex-row items-center">
                  <Image
                    source={{ uri: item.image }}
                    className="h-16 w-16 object-contain mr-2 rounded-md"
                  />
                  <View>
                    <Text className="text-lg font-semibold">{item.name}</Text>
                    <Text className="text-gray-400 font-bold">
                      $ {item.price}
                    </Text>
                  </View>
                </View>
                <View className="flex-1 flex flex-row justify-end items-end">
                  <TouchableOpacity
                    // 当商品数量为1时禁用减号按钮
                    disabled={item.quantity === 1}
                    onPress={() => handleDecrement(item.id)}
                  >
                    <AntDesign name="minuscircleo" size={30} color="black" />
                  </TouchableOpacity>
                  {/* 显示当前商品的数量,而非购物车总条目数 */}
                  <Text className="px-2">{item.quantity}</Text>
                  <TouchableOpacity onPress={() => handleIncrement(item)}>
                    <AntDesign name="pluscircle" size={30} color="#757575" />
                  </TouchableOpacity>
                </View>
              </View>
            </View>
          ))}
          <View className="flex-row items-center justify-between px-6 py-5">
            <Text className="text-lg font-semibold">总计:</Text>
            <Text className="text-lg font-semibold">${totalPrice.toFixed(2)}</Text>
          </View>
        </View>
      )}
    </SafeAreaView>
  );
};

export default CartScreen;

3. 可选:优化ProductDetailScreen添加逻辑

添加商品时可直接传入初始数量(reducer已兼容,不修改也能正常运行):

const addItemToCart = () => {
  dispatch(addToBasket({ id, image, name, price, quantity: 1 }));
  Toast.show({
    type: "success",
    text1: "商品已添加到购物车!",
    position: "bottom",
  });
};

内容的提问来源于stack exchange,提问作者Laspeed

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 02:07:01