Redux中不使用Immer如何通过展开运算符向嵌套state新增子数据
Redux原生实现嵌套Todo的ADD_SUBTODO更新逻辑
前置约定
你的ADD_SUBTODO action 的 payload 需要携带两个必要信息:
- 要追加子待办的父任务ID:
parentId - 完整的子待办项对象:
subTodo(包含id、name、complete字段)
payload 参考结构:
{ parentId: 1, subTodo: { id: 105, name: "subtask5", complete: false } }
正确实现代码
遵循Redux不可变更新原则,用ES6展开运算符逐层做浅拷贝即可,不需要引入深拷贝:
export default function reducer(state = initialState, action) { switch (action.type) { case ADD_TODO: const newTodo = state.todos.concat(action.payload); return { ...state, todos: newTodo }; case ADD_SUBTODO: const { parentId, subTodo } = action.payload; return { ...state, todos: state.todos.map(todo => { // 非目标父任务直接返回原引用,不做多余拷贝 if (todo.id !== parentId) return todo; // 匹配到目标父任务,拷贝后更新subdata数组 return { ...todo, subdata: [...todo.subdata, subTodo] }; }) }; // 其他case逻辑 default: return state; } }
实现逻辑说明
不可变更新的核心要求是绝不直接修改原状态的引用,每一层被修改的对象/数组都要生成新的引用,未修改的内容保持原引用即可,兼顾性能和更新正确性:
- 最外层通过
...state生成新的根状态对象 - 外层todos数组通过
map生成新数组,没有修改原数组;未命中的父任务直接返回原对象,避免不必要的拷贝和组件重渲染 - 命中的目标父任务通过
...todo生成新的父任务对象,没有修改原父任务的属性 - 父任务下的subdata数组通过展开运算符生成新数组,将新子待办追加到末尾,没有修改原subdata数组
避坑提醒
- 禁止直接调用
todo.subdata.push(subTodo)修改原数组,会导致状态引用未变化,Redux无法检测到更新,组件不触发重渲染 - 不需要用
JSON.parse(JSON.stringify(state))做全量深拷贝,性能差且会丢失特殊类型值,嵌套更新逐层浅拷贝是Redux官方推荐的原生写法 - 不要对所有todo项都做全量拷贝,未修改的项保持原引用可以大幅提升渲染性能
内容的提问来源于stack exchange,提问作者ismbyc
相关产品推荐
相关产品推荐

