You need to enable JavaScript to run this app. React Use Reducer开发模式触发两次导致布尔值切换后回退问题
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.28 19:54:03