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

为什么我的Redux Toolkit中equipItemById reducer出现状态突变问题?

问题原因

Redux Toolkit 内部依赖 Immer 库实现「mutable 写法更新不可变状态」的能力,而 Immer 默认只会代理普通 JavaScript 对象、数组这类可序列化结构,你存储在 itemsById 中的是自定义 Item 类的实例对象,Immer 不会对这类自定义类实例做变更跟踪,所以你直接修改 item.equipped = true 的操作不会被 Immer 捕获,自然也不会生成新的状态快照,表现出来就是属性修改不生效。

另外 Redux 官方本身也明确要求 state 中只能存储可序列化的普通值,禁止存储类实例、函数、Symbol 等不可序列化内容,你目前的用法本身就违反了 Redux 的最佳实践。

解决方案

  • 方案一(最推荐):把所有存入 Redux state 的 Item 实例转为普通对象
    初始化的时候可以用展开运算符转换:
    const initialState = {
      itemsById: [
        {...new Item('weapon', 'oak_stave', 0)},
        // 其余 Item 实例同理转换
      ],
      itemsByLocationName: {
        centralSquare: [{...new Item('weapon', 'enchanted_steel_sword')}],
      },
    };
    
    新增 Item 的时候也做同样的转换,确保 state 中不存在自定义类实例即可,原有 reducer 的可变写法不需要调整。
  • 方案二(不推荐):给 Item 类添加 Immer 可识别标记
    如果一定要保留类实例结构,可以给 Item 类添加 Immer 的可代理标记:
    import { immerable } from 'immer';
    
    class Item {
      [immerable] = true;
      // 原有类逻辑保持不变
    }
    
    这种方式可以让 Immer 识别到自定义类实例并跟踪变更,但会破坏 Redux 状态的可序列化特性,可能导致时间旅行、状态持久化等功能失效。

额外注意项

你代码中的 addItemToActorFromLocationByIdAndName reducer 存在外部对象突变风险:你直接修改了 action.payload.item 的 ownerId 属性,action.payload 是从组件层传入的外部对象,直接修改会导致原对象被意外变更,建议修改前先做浅拷贝:

addItemToActorFromLocationByIdAndName: (state, action) => {
  const { actorId, item } = action.payload;
  const itemCopy = {...item}; // 先拷贝再修改
  itemCopy.ownerId = actorId;
  state.itemsById.push(itemCopy);
},

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 00:45:03