React Use Reducer开发模式触发两次导致布尔值切换后回退问题
React useReducer 布尔值双重切换问题解决方案
问题根因
- 现有reducer实现不符合React不可变更新规范:仅对state数组做了浅拷贝,数组内的元素仍保留对原state对象的引用,直接修改
item.active本质是篡改了原state的属性,不是纯函数实现。 - React StrictMode在开发环境下会对reducer做双重调用校验:该机制的作用是排查非纯的逻辑实现。由于你的reducer存在修改原对象的副作用,第一次调用时直接改写了原对象的
active值,第二次调用时基于已被篡改的原对象再次执行取反逻辑,最终状态回到初始值,表现为dispatch触发后状态没有变化。
修正思路
- 严格遵守纯函数要求:更新状态时对涉及修改的对象做副本处理,全程不修改传入的state入参,所有变更都在新生成的副本上完成。
- 替换掉依赖取反的切换逻辑:既然事件层已经明确知道当前checkbox的选中/未选中状态,直接在action中传递目标布尔值,让reducer做确定性的状态赋值,避免重复取反带来的不可控问题。
- 补全reducer中漏处理的
ACTIONS.UNSELECTED分支逻辑。
修正后代码示例
基础常量与reducer实现
// 统一定义action类型常量 const ACTIONS = { INIT: 'init', SELECTED: 'selected', UNSELECTED: 'unselected' } function reducerBalls(state: lotteryBalls[], action: any) { switch (action.type) { case ACTIONS.INIT: return action.balls; case ACTIONS.SELECTED: // 数组map生成新数组,仅对需要更新的索引位置做对象浅拷贝 return state.map((item, index) => { if (index === action.index) { return { ...item, active: true } } return item }) case ACTIONS.UNSELECTED: // 逻辑同SELECTED分支,设置目标active值为false return state.map((item, index) => { if (index === action.index) { return { ...item, active: false } } return item }) default: return state; } }
事件处理逻辑优化
function ballCheckboxHandler(ball: lotteryBalls, event: any) { const actionType = event.target.checked ? ACTIONS.SELECTED : ACTIONS.UNSELECTED return dispatch({ type: actionType, index: ball.number }) }
效果说明
修正后的reducer是完全的纯函数,不会修改传入的state入参,哪怕开发环境下StrictMode连续两次调用同一个reducer,两次返回的状态结果完全一致,不会出现状态来回切换的问题,不需要为了调试关闭StrictMode。
内容的提问来源于stack exchange,提问作者Sukh_Bains
相关产品推荐
相关产品推荐

