Redux Toolkit购物车报错:"count"只读,重复添加商品失败
问题分析:添加购物车后清空再添加报错"count is read-only"
问题现象
首次将商品添加到购物车时,slice中的action.payload.count = 1;可正常运行;但清空购物车(移除已添加商品)后再次添加同一商品,控制台会抛出错误:Uncaught TypeError: "count" is read-only addToCart CartSlice.js:13,商品无法加入购物车;添加未删除过的新商品则无此问题。
相关代码
cartSlice.js
export const CartSlice = createSlice({ name: "cart", initialState: [], reducers: { addToCart(state, action){ const existIndex = state.findIndex(item => item.shortName === action.payload.shortName); if(existIndex !== -1){ state[existIndex].count += 1; } else{ action.payload.count = 1; state.push(action.payload); } }, clearCart(state, action){ return state=[]; } } })
card.js
export const Card = ({product, showProductSlide}) => { const dispatch = useDispatch(); const addToCart = () => { dispatch({type: "cart/addToCart", payload: product}) } return( <div className="card item border-0 overflow-hidden"> <div className="img-box"> <img src={product.pic} className="card-img-top" alt="..." /> <div className="triangle"></div> </div> <div className="card-body d-flex flex-column justify-content-between bg-secondary text-white"> <p className="card-text">{product.shortName}</p> <p className="card-text text-end fw-bold">{product.price}</p> <div className="d-flex justify-content-between"> <button className="btn btn-sm bg-transparent text-white" onClick={showProductSlide}><span className="q-mark me-1">?</span>Quick-view</button> <button className="btn btn-sm bg-transparent btn-cart text-nowrap" onClick={addToCart}>Add to cart<div><span>+</span></div></button> </div> </div> </div> ) }
cartList.js
export const CartList = ({showCart, cartProducts}) => { const dispatch = useDispatch(); const totalPrices = () => { let total = 0; cartProducts.forEach(function (value){ total += Number(value.price.replace(/[$]/, "")) * value.count; }) return total.toFixed(2); } const clearCart = () => { dispatch({type: "cart/clearCart", payload: ""}) } return( <div className={showCart ? "open-list" : "close-list"}> {cartProducts.length === 0 ? <span className="align-self-center">empty</span> : <> <ul> {cartProducts.map((item, index) => { return( <li className="d-flex justify-content-between" key={index}> <span>{item.shortName}</span><span>{item.count}</span> </li> ) } )} </ul> <p className="total-price">Total Amounts: {totalPrices()}</p> <button onClick={clearCart}>Cancel order</button> </> } </div> ) }
问题原因
核心问题是直接修改了Redux传递的action.payload对象,该对象已被Redux Toolkit的Immer冻结:
- 首次添加时,
product是未经过Redux处理的原始数据(比如静态数据或API返回值),此时对象未被冻结,可修改count属性; - 当该对象被加入Redux state后,Immer会自动冻结state中的所有对象,确保状态不可变;
- 清空购物车只是将state置空,但之前加入过state的
product对象已经被冻结;再次点击添加时,dispatch传递的还是同一个被冻结的对象,此时修改它的count属性就会触发只读错误。
解决方案
不要直接修改action.payload,而是创建原对象的浅拷贝,添加count属性后再加入state:
修改cartSlice.js中的addToCart方法:
addToCart(state, action){ const existIndex = state.findIndex(item => item.shortName === action.payload.shortName); if(existIndex !== -1){ state[existIndex].count += 1; } else{ // 创建新对象,避免修改原冻结对象 state.push({...action.payload, count: 1}); } }
同时简化clearCart的写法:
clearCart(state, action){ return []; }
原理说明
通过扩展运算符...创建原对象的浅拷贝,新对象未被冻结,可正常添加count属性并加入state,既满足Redux的不可变状态要求,又避免了修改冻结对象导致的错误。
内容的提问来源于stack exchange,提问作者Arman Ebrahimi
相关产品推荐
相关产品推荐

