为什么我的Redux Toolkit中equipItemById reducer出现状态突变问题?
问题原因
Redux Toolkit 内部依赖 Immer 库实现「mutable 写法更新不可变状态」的能力,而 Immer 默认只会代理普通 JavaScript 对象、数组这类可序列化结构,你存储在 itemsById 中的是自定义 Item 类的实例对象,Immer 不会对这类自定义类实例做变更跟踪,所以你直接修改 item.equipped = true 的操作不会被 Immer 捕获,自然也不会生成新的状态快照,表现出来就是属性修改不生效。
另外 Redux 官方本身也明确要求 state 中只能存储可序列化的普通值,禁止存储类实例、函数、Symbol 等不可序列化内容,你目前的用法本身就违反了 Redux 的最佳实践。
解决方案
- 方案一(最推荐):把所有存入 Redux state 的 Item 实例转为普通对象
初始化的时候可以用展开运算符转换:
新增 Item 的时候也做同样的转换,确保 state 中不存在自定义类实例即可,原有 reducer 的可变写法不需要调整。const initialState = { itemsById: [ {...new Item('weapon', 'oak_stave', 0)}, // 其余 Item 实例同理转换 ], itemsByLocationName: { centralSquare: [{...new Item('weapon', 'enchanted_steel_sword')}], }, }; - 方案二(不推荐):给 Item 类添加 Immer 可识别标记
如果一定要保留类实例结构,可以给Item类添加 Immer 的可代理标记:
这种方式可以让 Immer 识别到自定义类实例并跟踪变更,但会破坏 Redux 状态的可序列化特性,可能导致时间旅行、状态持久化等功能失效。import { immerable } from 'immer'; class Item { [immerable] = true; // 原有类逻辑保持不变 }
额外注意项
你代码中的 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
相关产品推荐
相关产品推荐

