Redux中删除嵌套对象键时原对象意外被修改的问题求助
解决Redux中不可变状态下删除嵌套键的问题
嗨,这个问题我太熟了——你踩了浅拷贝的坑!咱们来一步步拆解:
为什么原对象会被修改?
Object.assign({}, t1)做的是浅拷贝:它只会复制对象第一层的属性值,对于嵌套的a对象,其实只是把t1.a的引用复制给了t2.a。也就是说,t1.a和t2.a指向的是内存里同一个对象,所以你删除t2.a.a1的时候,原对象的t1.a自然也会跟着变,这完全违反了Redux状态不可变的要求。
几种可行的解决办法
1. 手动逐层拷贝嵌套对象
如果你的状态结构比较简单,能明确嵌套层级,可以手动拷贝需要修改的嵌套对象:
let t1 = {a: { a1: 'test' }} // 不仅拷贝外层,还要单独拷贝内层的a对象 let t2 = Object.assign({}, t1, { a: Object.assign({}, t1.a) }) delete t2['a']['a1'] console.log(t1) // { a: { a1: 'test' } } 符合预期! console.log(t2) // { a: {} } 符合预期!
2. 用ES6扩展运算符(更简洁)
扩展运算符...也是浅拷贝,但可以嵌套使用来处理多层结构,写法比Object.assign更清爽,Redux里很常用:
let t1 = {a: { a1: 'test' }} // 外层拷贝,同时内层的a也重新拷贝一份 let t2 = { ...t1, a: { ...t1.a } } delete t2.a.a1 // 同样t1不会被修改
3. JSON序列化深拷贝(适合简单场景)
如果你的状态里没有函数、Symbol类型,也没有循环引用,可以用JSON的方式做深拷贝:
let t1 = {a: { a1: 'test' }} // 先转成JSON字符串,再解析成新对象,彻底断开所有引用 let t2 = JSON.parse(JSON.stringify(t1)) delete t2.a.a1
⚠️ 注意:这种方法有局限性,比如函数、Symbol、undefined会被忽略,循环引用的对象会直接报错,所以只适合纯数据的简单结构。
4. 用Immer(推荐!Redux生态标配)
如果你用的是Redux Toolkit(现在官方推荐的Redux写法),Immer已经内置了;如果是传统Redux,也可以单独安装Immer。它允许你用“看起来可变”的写法,自动帮你生成不可变的新状态:
import { produce } from 'immer' let t1 = {a: { a1: 'test' }} // draft是临时的“可变”副本,修改它不会影响原对象 let t2 = produce(t1, draft => { delete draft.a.a1 }) console.log(t1) // { a: { a1: 'test' } } console.log(t2) // { a: {} }
这种方式不管嵌套层级有多深,都不用手动一层层拷贝,写起来特别省心,也是现在Redux里处理不可变状态的最佳实践。
核心思路总结
本质上就是要断开嵌套对象的引用关系,确保你修改的是新对象的嵌套属性,而不是原对象的引用。根据你的项目情况选上面的方法就行~
内容的提问来源于stack exchange,提问作者Atopus
相关产品推荐
相关产品推荐

