React严格模式下useReducer触发两次致购物车数量重复累加问题
React严格模式下购物车重复累加问题修复
问题表现
- 开启React严格模式时,开发环境会重复执行reducer等逻辑检测非纯副作用,在不关闭严格模式的前提下,需要保证重复执行逻辑时输出结果一致
- 购物车添加商品场景:添加数量为3的商品时,逻辑重复执行后最终数量计算为
1 + 3 + 3 =7,不符合预期 - 原有逻辑:购物车无对应商品时通过
concat()追加商品项;存在对应商品时定位到对应项修改数量,重复执行时累加逻辑同步执行两次导致数值错误
问题根因
Reducer必须是纯函数,原有代码违反了纯函数要求:
- 更新已存在商品时,直接修改了原state中的
items数组:const stateItems = state.items; stateItems[itemIndex] = updatedItem是对原状态的直接修改,属于副作用 - 严格模式第一次执行reducer时已经修改了原state的商品数量,第二次执行会基于第一次修改后的结果再次累加,最终导致数量翻倍
- 存在隐藏逻辑bug:未匹配到对应action类型时直接返回
defaultCart,会导致非SUBMIT操作时购物车被意外清空
修复后代码
export const CartContextProvider = props => { const defaultCart = { items: [], qty: 0, total: 0 } const cartReducer = (state, actions) => { if (actions.type === 'SUBMIT') { const { id, qty: addQty, price } = actions.item; const addNum = +addQty; const unitPrice = +price; // 非法数量直接返回原状态不更新 if (addNum <= 0) return state; // 查找商品位置 const itemIndex = state.items.findIndex(el => el.id === id); // 商品不存在:生成新数组追加新商品 if (itemIndex === -1) { const newItem = { id: id, qty: addNum, price: unitPrice }; return { items: [...state.items, newItem], qty: state.qty + addNum, total: state.total + addNum * unitPrice }; } // 商品已存在:不可变更新对应项 else { const existItem = state.items[itemIndex]; const updatedItem = { ...existItem, qty: existItem.qty + addNum }; // 生成新的数组副本,不修改原数组 const updatedItems = [...state.items]; updatedItems[itemIndex] = updatedItem; return { items: updatedItems, qty: state.qty + addNum, total: state.total + addNum * unitPrice }; } } // 未匹配action返回当前状态,避免清空购物车 return state; } const [cart, dispatchCart] = useReducer(cartReducer, defaultCart) function cartSubmitHandler (id, qty, price) { dispatchCart({ type: 'SUBMIT', item: { id, qty, price } }) } return ( <CartContext.Provider value={{ cart: cart, cartSubmitHandler: cartSubmitHandler }} > {props.children} </CartContext.Provider> ) } export default CartContext
修复说明
- 所有状态更新遵循不可变原则:对象、数组更新时都生成新的副本,绝不直接修改传入的state参数,保证reducer是纯函数——相同输入无论执行多少次,输出结果完全一致,从根本上适配严格模式的重复检测逻辑
- 提前拦截非法数量参数,减少无效更新
- 修复了action不匹配时清空购物车的隐藏bug
- 移除了调试用的console.log,避免严格模式下重复打印干扰判断
内容的提问来源于stack exchange,提问作者Miki U
相关产品推荐
相关产品推荐

