如何在reducer中向嵌套对象添加新对象并解决状态被覆盖的问题
问题根因
你当前代码存在3个核心错误,直接导致状态被覆盖:
- 大小写不匹配:初始状态属性是小写
items,reducer里操作的是大写Items,属性不一致导致读取旧状态失效 - 引用了不存在的状态属性:代码中
state.exercises[action.grandParentId]无对应初始定义,读取永远返回undefined,每次展开都是空数组,自然相当于覆盖旧内容 - 嵌套结构更新逻辑错误:你当前写法每次往
grandParentId对应的数组里塞入仅包含当前idparent的新对象,没有对已存在的同idparent项做合并更新,旧的同idparent内容自然会被替换
修复后的ADD_ITEM分支代码
// ADD ITEM case ADD_ITEM: // 取出当前grandParentId对应的数组,不存在则初始化为空 const curGrandList = state.items[action.grandParentId] ?? []; // 查找数组中已存在的对应idparent项的位置 const existParentIdx = curGrandList.findIndex(item => Object.hasOwn(item, action.idparent)); let newGrandList; if (existParentIdx > -1) { // 已存在对应idparent,往其下的子数组追加新内容 const existParent = curGrandList[existParentIdx]; const updatedParent = { [action.idparent]: [ ...existParent[action.idparent], { index: action.index, id: action.id, item: action.item, } ] }; // 生成新的祖父级数组,替换对应位置的父项 newGrandList = [ ...curGrandList.slice(0, existParentIdx), updatedParent, ...curGrandList.slice(existParentIdx + 1) ]; } else { // 不存在对应idparent,新增父项到数组末尾 newGrandList = [ ...curGrandList, { [action.idparent]: [ { index: action.index, id: action.id, item: action.item, } ] } ]; } return { ...state, items: { ...state.items, [action.grandParentId]: newGrandList } };
Reducer状态更新通用规范
- 严格遵守不可变原则:所有更新都要返回全新的状态对象,不能直接修改原state的任何层级属性,普通对象/数组用展开运算符,复杂嵌套结构可以用immer库简化操作
- 属性名完全对齐:大小写、拼写要和初始状态完全匹配,避免读取不到旧状态的问题
- 层级更新由深到浅:嵌套结构更新要先修改最深层的属性,再逐层往上生成新的父对象/数组,避免浅拷贝导致深层引用依然指向原状态
- 仅操作已定义字段:更新时只能操作初始状态中明确定义的合法字段,避免冗余数据和读取异常
- 优先扁平化状态结构:多层嵌套的状态更新逻辑非常容易出错,推荐把嵌套数据扁平化为多个实体表,用id做关联,更新逻辑会大幅简化
内容的提问来源于stack exchange,提问作者Richardson
相关产品推荐
相关产品推荐

