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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 07:15:56