状态机state如果是对象数组,就不能进行浅拷贝吗?
-
今天在开发的时候发现的一个奇怪的现在,如果状态机是一个数组,数组内是对象Object的集合,尝试了各种方法都不能进行浅拷贝。
简单的用一个按钮执行点击函数进行测试。/*构造函数中设置两个状态机变量,一个是数组一个是数字*/ constructor(props){ super(props); this.state={ addressData:[{key:1,contact: 1, address: "测试地址1",isDefault:3},], abc:0 } } /*随便写个按钮button,这里通过一个简单的点击函数改变变量的值*/ onClickDefaultAddress(){ let temp2= [...this.state.addressData]; /*数组解构赋值,百度说这样可以浅拷贝*/ temp2[0].address="临时变量改变地址"; let temp_abc = this.state.abc; temp_abc++; /*打印输出*/ console.log(temp_abc, this.state.abc); console.log(temp2[0].address, this.state.addressData[0].address); }
然后打印输出,结果是下面这样的:
可以看到 let temp2= [...this.state.addressData]这段代码实际进行了深拷贝,temp2只是一个指针,指向了与this.state.addressData一样的地址。并没有得到我想要的是指向新的地址的一个新对象。
使用ES5的写法,const temp2= this.state.addressData.concat( ),结果与上面是一样的。
如果把上面的数字状态机变量换成数组,如下:
/*构造函数*/ constructor(props){ super(props); this.state={ addressData:[{key:1,contact: 1, address: "测试地址1",isDefault:3},], abc:[1,2,3,4,5] } } /*按钮点击函数*/ onClickDefaultAddress(index){ let temp2= [...this.state.addressData]; temp2[0].address="临时变量改变地址"; let temp_abc = this.state.abc; temp_abc[0]=0; console.log(temp_abc, this.state.abc); console.log(temp2[0].address, this.state.addressData[0].address); }
打印的结果是这样的:
这样的数字数组又可以进行浅拷贝。这个结果可是说是非常恶心了。
难道是我的使用方法不对?有没有人解答一下怎么浅拷贝这个对象数组状态机。
-
你自己的论述矛盾挺多的,我从头简单说下吧
- 在JS中,除了数字、字符串、布尔、null、undefined以外的都是
引用
类型数据,包括对象、数组、函数 引用
类型的数据用等号
就是浅拷贝
。对于数组来说,[...]和concat都是深拷贝
- 那么为啥深拷贝看起来还是可以修改原先的数据?
把你的
装有对象的数组
比作超市里五包装的红烧牛肉面
。数组
就是五小包外面的大包装
,你深拷贝数组的操作,实际是把大包装换掉——然而里面的五小包面,仍然是原来的五小包面啊!- 但是数字数组又不一样了。因为数字
不是
引用型数据,所以数字数组就只有一层包装。 - 彻底的深拷贝,就是要把
所有的包装都换掉
。当然我们几乎不会用到这种显然特别消耗的操作。 - react/react native中讨论这个的意义在于如何快速获知数据的变化。当我们根据自己的需要setState的时候,是否考虑过其内部如何知道我们改了哪些数据呢?一个可能令人震惊的事实是,标准的component压根就不检查你改过什么数据,始终重新rerender,哪怕你setState是完全一模一样的数据,这样来保证100%在任何情况下都可以刷新。但是在react native的列表组件或是react的purecomponent中,由于对渲染性能要求较高,它会对数据源进行检查,怎么检查呢?用
===
号检查。这样的问题就在于,如果你数据源是一个数组,你对数组内部直接修改,其包装
上是看不出变化的。 - 最直接的实践原则就是,任何时候碰到一个
有包装
的数据,先换包装再修改。而且是只换你需要换的包装,更深的不管。如果你要给数组增删数据,ok,那就只换数组大包装。如果你要修改数组内部的元素,那么就得先换大包装
,再换小包装
,最后修改。
- 在JS中,除了数字、字符串、布尔、null、undefined以外的都是
-
像FlatList的套路就是这样
第一行换大包装,第三四行换小包装,最后放回列表里,于是它看到外包装换了,然后里面某个元素的包装也换了,于是进行
对应的最小的更新
。
-
@晴明 你好,我在使用状态机的时候,也遇到了问题 ,就是对象数组更新了,但是界面没更新,看到你的回复,我用了复制数组的方法也没有用,请问我要增加数据的话,要怎么做呢?
代码如下:
const data = [...this.state.data]; data.unshift(对象); this.setState({data});
-
@agoninemo this.setState({data:[].concat(data)})
-
@agoninemo 你得贴更完整的代码
-
@mirghojam 用...复制即可,不需要再concat
-
let temp2 = JSON.parse(JSON.stringify(this.state.addressData))