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

Redux Toolkit触发Immer修改draft同时返回新值报错排查

Immer 报错根因

你遇到的报错:

An immer producer returned a new value and modified its draft. Either return a new value or modify the draft.

是Redux Toolkit内置的Immer状态更新规则限制:同一个reducer里,你只能选择两种更新模式的其中一种,不能同时使用:

  1. 直接修改Immer包装的draft状态对象,不手动return新值
  2. 不修改draft,完全自己构造新的状态对象return

你之前认为filter、map不会修改原始状态的认知,在普通JS场景下是对的,但在createSlice的reducer里不成立:这里传入的state是Immer生成的Proxy代理对象,从state上通过filter、find拿到的子元素也都是代理对象,你执行currentArray[0].votes += 1的时候,已经在直接修改draft状态了;但你后续又自己构造了sortedArray作为返回值,相当于同时触发了两种更新模式,直接触发报错。


代码里的具体问题点
  • const currentArray = state.filter(...)拿到的数组元素是Immer代理的draft对象,不是普通JS对象,修改它的属性就属于修改draft
  • 修改draft后又返回自定义的新数组,违反Immer更新规则
  • 逻辑问题:_.sortBy默认是升序排序,票数高的内容会排到数组末尾,不符合投票榜单的常规展示逻辑
  • 逻辑bug:createNotificationreducer直接return action.payload会把整个轶事列表的状态替换成通知内容,通知逻辑应该单独建slice存储,不要和轶事数据放在同一个状态节点下

修复方案

二选一即可,第一种是Redux Toolkit官方推荐的写法,代码更简洁:

方案1:直接修改draft(推荐)

不需要手动构造新数组、不需要return,Immer会自动根据你的修改生成不可变状态:

increaseVote(state, action) {
  // 找到目标投票对象
  const target = state.find(item => item.id === action.payload)
  if (target) {
    target.votes += 1
  }
  // 直接在draft上排序,b.votes - a.votes是降序,票数高的在前
  state.sort((a, b) => b.votes - a.votes)
}

方案2:返回全新状态(不修改draft)

全程不要直接修改从state里拿到的对象属性,所有更新都通过返回新对象实现:

increaseVote(state, action) {
  // 生成更新后的数组,所有修改都返回新对象,不触碰原draft的属性
  const updatedList = state.map(item => {
    if (item.id === action.payload) {
      // 用展开运算符生成新对象,不要直接写item.votes +=1
      return { ...item, votes: item.votes + 1 }
    }
    return item
  })
  // 排序后返回,要降序就加.reverse()
  return _.sortBy(updatedList, 'votes').reverse()
}

内容的提问来源于stack exchange,提问作者SupraCoder

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 15:39:14