React中reducer函数多次调用致计数异常,如何解决?
修复useReducer计数增量错误(每次加2)的问题
问题根源
- Reducer中直接修改原状态:
increment-quantity逻辑里直接修改state[action.mealIndex][1]属于状态突变,违反了React对reducer纯函数的要求。在Strict Mode开发环境下,双重调用reducer会因为状态突变导致计数被重复修改。 - Quantity状态更新依赖旧值:
setQuantity(Quantity+1)基于当前渲染周期的旧值更新,Strict Mode下组件双重渲染会让这个操作执行两次,导致每次加2。 - 冗余的Quantity状态:
Quantity可直接从orderedMeals计算得到,单独维护容易出现状态不同步。
修复方案
1. 重构Reducer为纯函数
确保所有状态操作都返回新的数组/对象,不修改原state:
function reducer(state, action) { switch (action.type) { case "increment-quantity": return state.map((mealPair, index) => { if (index === action.mealIndex) { // 返回新的mealPair数组,不修改原元素 return [mealPair[0], mealPair[1] + 1]; } return mealPair; }); case "decrement-quantity": return state.map((mealPair, index) => { if (index === action.mealIndex && mealPair[1] > 1) { return [mealPair[0], mealPair[1] - 1]; } return mealPair; }); case "add-meal": return [...state, [action.meal, 1]]; default: return state; } }
2. 移除冗余的Quantity状态,改为计算值
直接从orderedMeals计算总数量,避免状态不同步:
function App() { const [orderedMeals, dispatch] = useReducer(reducer, []); const [openModal, setOpenModal] = useState(false); const [isFetching, menu, setFetchedData, error] = useFetch(fetchMeals, []); // 直接计算总数量,无需单独维护状态 const totalQuantity = orderedMeals.reduce((sum, [_, count]) => sum + count, 0); const onAddToCart = (meal) => { const mealFound = orderedMeals.find((mealPair) => mealPair[0] === meal); if (!mealFound) { dispatch({ type: "add-meal", meal: meal }); } else { const mealIndex = orderedMeals.indexOf(mealFound); dispatch({ type: "increment-quantity", mealIndex: mealIndex }); } }; // 后续使用totalQuantity即可 }
3. (可选)保留Quantity状态的函数式更新写法
若必须单独维护Quantity,改用函数式更新确保基于最新状态:
const onAddToCart = (meal) => { // ... 其他逻辑不变 setQuantity(prevQuantity => prevQuantity + 1); };
关于Strict Mode的影响
Strict Mode在开发环境下会双重调用组件函数、reducer等,用于检测不纯操作。原代码的状态突变导致第一次调用reducer时修改了原state,第二次调用又基于已修改的state再次增量,最终出现加2的错误。修复纯函数后,两次调用都会基于原始state生成新状态,最终只会生效一次。
内容的提问来源于stack exchange,提问作者Ragnar
相关产品推荐
相关产品推荐

