React中使用Object.assign处理state未达预期问题排查
嘿,我来帮你理清楚这个问题——你猜的没错,确实是Object.assign的特性导致的,但核心原因是它只做浅拷贝,我们一步步拆解:
首先看你的代码逻辑:你想先把当前的geoJson存到prevGeoJson里,再修改新的geoJson,最后更新状态。但Object.assign({}, this.state.geoJson)只会复制geoJson的第一层属性,而geoJson.features是一个数组(引用类型),所以prevGeoJson.features和geoJson.features指向的是同一个数组对象。
当你执行geoJson.features.push(data)的时候,你其实是在修改这个共享的数组——那prevGeoJson.features自然也会跟着变,最后两个对象看起来就完全一样了,这可不是你想要的“捕获时刻M的状态”效果。
那怎么解决呢?我们需要对嵌套的引用类型也做拷贝,确保prevGeoJson和geoJson的features是两个独立的数组,这里给你几个实用的方案:
方案1:手动拷贝嵌套数组(最直接)
既然问题出在features数组,我们单独复制它就行,用ES6的扩展运算符很方便:
// 复制原geoJson的同时,单独复制features数组,确保prevGeoJson拿到的是时刻M的状态 const prevGeoJson = { ...this.state.geoJson, features: [...this.state.geoJson.features] }; // 直接创建带新数据的geoJson,同样复制数组并添加新元素 const geoJson = { ...this.state.geoJson, features: [...this.state.geoJson.features, data] }; this.setState({ prevGeoJson, geoJson, currentMarker: data });
这样prevGeoJson.features和geoJson.features就是两个完全独立的数组,修改其中一个不会影响另一个。
方案2:JSON序列化深拷贝(适合简单结构)
如果你的geoJson里没有函数、Symbol这类无法被JSON序列化的内容,这种方式可以快速实现深拷贝:
// 用JSON序列化再反序列化,彻底复制整个对象 const prevGeoJson = JSON.parse(JSON.stringify(this.state.geoJson)); const geoJson = JSON.parse(JSON.stringify(this.state.geoJson)); geoJson.features.push(data); this.setState({ prevGeoJson, geoJson, currentMarker: data });
注意:如果对象里有循环引用、函数或者特殊类型,这种方法会丢失这些内容,所以只适合结构简单的场景。
方案3:用不可变数据库(复杂项目推荐)
如果你的项目经常需要处理这类状态更新,推荐用Immer或者Immutable.js,它们能让你用“看起来可变”的写法,实际生成不可变的状态,避免手动拷贝的麻烦。比如用Immer的话:
import produce from 'immer'; this.setState(produce(draft => { // 先复制当前geoJson到prevGeoJson draft.prevGeoJson = JSON.parse(JSON.stringify(draft.geoJson)); // 直接修改draft里的geoJson,Immer会帮你生成新的不可变对象 draft.geoJson.features.push(data); draft.currentMarker = data; }));
当然这个需要额外安装依赖,适合状态结构复杂的项目。
总结一下:核心问题就是浅拷贝导致嵌套的引用类型共享内存,只要确保嵌套的数组/对象也被复制成独立实例,就能解决这个问题啦!
内容的提问来源于stack exchange,提问作者Jerlam

