Redux购物车状态为空及Local Storage商品数量更新异常求助
Redux购物车问题解决方案
问题1:购物车状态始终为空,无法保留商品
问题分析
- Reducer初始状态未从LocalStorage加载:当前
cartReducer初始状态为{cartItems:[]},未读取LocalStorage中保存的购物车数据,导致页面刷新后购物车为空。 - 现有商品数量未正确递增:添加已存在的商品时,Reducer直接用新payload替换原有商品,但新payload的quantity是传入的初始值(而非原有数量+1),导致数量未更新。
- 组件未正确读取购物车数据:原代码中仅获取整个state对象,未直接提取
cartItems,且渲染逻辑被注释,无法展示商品。
修复步骤
- 从LocalStorage初始化Reducer状态:
修改CartReducer.js,读取LocalStorage中的购物车数据作为初始状态:
const initialState = { cartItems: localStorage.getItem('cartItems') ? JSON.parse(localStorage.getItem('cartItems')) : [] }; export const cartReducer = (state = initialState, action) => { // 原有逻辑 };
- 修正现有商品的数量递增逻辑:
在Reducer中,当商品已存在时,将原有数量加上新传入的quantity:
case ADD_TO_CART: const item = action.payload; const existItem = state.cartItems.find(x => x.product === item.product); if(existItem){ return { ...state, cartItems: state.cartItems.map(x => x.product === existItem.product ? {...x, quantity: x.quantity + item.quantity} : x ) }; } else { return { ...state, cartItems: [...state.cartItems, item] }; }
- 修正组件中对cartItems的读取:
直接从Redux状态中提取cartItems,并恢复渲染逻辑:
const { cartItems } = useSelector(state => state.cart); console.log(cartItems); // 现在应正确显示购物车商品
问题2:LocalStorage处理重复商品时逻辑异常
问题分析
- setLocalStorage函数参数处理错误:原代码通过
item[0][0]获取新商品,这是错误的——getState().cart.cartItems是数组,item[0][0]会取数组第一个元素的第一个属性,无法正确识别当前添加的商品。 - 重复商品逻辑错误:原代码直接push新商品到数组,导致重复条目;之前的尝试代码中,遍历非匹配商品时错误地将quantity设为1,破坏原有数据。
修复步骤
- 重写setLocalStorage函数:
接收当前要添加的商品,正确处理重复商品的数量递增:
const setLocalStorage = (newItem) => { let cartItems = localStorage.getItem('cartItems') ? JSON.parse(localStorage.getItem('cartItems')) : []; const existItem = cartItems.find(x => x.product === newItem.product); if(existItem){ cartItems = cartItems.map(x => x.product === existItem.product ? {...x, quantity: x.quantity + newItem.quantity} : x ); } else { cartItems.push(newItem); } localStorage.setItem('cartItems', JSON.stringify(cartItems)); };
- 修正action中的调用:
直接传入当前添加的商品到setLocalStorage,而非获取整个cartItems数组:
export const addItemsToCart = (id, quantity) => async(dispatch) => { const {data} = await axios.get(`http://localhost:3000/api/v1/products/${id}`); const newItem = { product: data.product._id, name: data.product.name, price: data.product.price, image: data.product.images[0].url, stock: data.product.Stock, quantity, }; dispatch({ type: ADD_TO_CART, payload: newItem, }); setLocalStorage(newItem); };
完整修正代码
CartReducer.js
import { ADD_TO_CART } from "../constants/cartConstants"; const initialState = { cartItems: localStorage.getItem('cartItems') ? JSON.parse(localStorage.getItem('cartItems')) : [] }; export const cartReducer = (state = initialState, action) => { switch (action.type) { case ADD_TO_CART: const item = action.payload; const existItem = state.cartItems.find(x => x.product === item.product); if(existItem){ return { ...state, cartItems: state.cartItems.map(x => x.product === existItem.product ? {...x, quantity: x.quantity + item.quantity} : x ) }; } else { return { ...state, cartItems: [...state.cartItems, item] }; } default: return state; } };
cartAction.js
import { ADD_TO_CART } from "../constants/cartConstants"; import axios from "axios"; const setLocalStorage = (newItem) => { let cartItems = localStorage.getItem('cartItems') ? JSON.parse(localStorage.getItem('cartItems')) : []; const existItem = cartItems.find(x => x.product === newItem.product); if(existItem){ cartItems = cartItems.map(x => x.product === existItem.product ? {...x, quantity: x.quantity + newItem.quantity} : x ); } else { cartItems.push(newItem); } localStorage.setItem('cartItems', JSON.stringify(cartItems)); }; export const addItemsToCart = (id, quantity) => async(dispatch) => { const {data} = await axios.get(`http://localhost:3000/api/v1/products/${id}`); const newItem = { product: data.product._id, name: data.product.name, price: data.product.price, image: data.product.images[0].url, stock: data.product.Stock, quantity, }; dispatch({ type: ADD_TO_CART, payload: newItem, }); setLocalStorage(newItem); };
Cart组件(Cart.js)
import React,{useState} from 'react' import MetaData from '../layout/metaData' import CartItemCard from '../cart/CartItemCard' import { motion } from 'framer-motion' import Footer from '../layout/Footer' import NoItem from '../layout/noItem.js' import { useSelector,useDispatch } from 'react-redux' export default function Cart() { const dispatch = useDispatch(); const { cartItems } = useSelector(state => state.cart); console.log(cartItems); const [display,setDisplay] = useState('none'); const head = { product:'Product', price:'Price', name:"amarajs", quantity:1, } return ( <> <MetaData title = "Your Cart" /> <div className = 'container mx-auto my-[16rem] flex flex-col justify-center'> {cartItems.length === 0 ? <NoItem/> : ( <div className = {`flex flex-col justify-center items-center gap-[3.2rem] blur-${display} mb-[5.4rem]`}> <CartItemCard heading = {head}/> {cartItems.map((item)=>( <CartItemCard item = {item} key={item.product}/> ))} </div> )} <hr className = "p-4 container text-black mb-[3.2rem]"/> <div className = "px-[5.4rem] sub-total--section flex flex-col items-end self-end mb-[3.2rem]"> <div className = "flex justify-center items-end gap-[1rem]"> <p className = "text-[2.4rem]"> SUBTOTAL </p> <p className = "total--price text-[3.2rem] font-semibold"> ₹{cartItems.reduce((acc, item) => acc + item.price * item.quantity, 0)} </p> </div> <p className = "total-subhead text-[#555] text-[1.6rem] italic"> Shipping & taxes calculated at checkout </p> </div> <motion.button whileHover={{scale:1.04}} className = " self-center text-[2.4rem] add-to-cart h-[6.4rem] w-[300px] bg-[rgb(14,14,14)] text-[#fff] rounded-[5rem] hover:bg-[#fff] hover:text-[rgb(14,14,14)] mb-[4.4rem]" disabled={cartItems.length === 0} > checkout </motion.button> </div> <Footer/> </> ) }
内容的提问来源于stack exchange,提问作者Saxena Siddharth
相关产品推荐
相关产品推荐

