ReactJS:提取子数组修改后父数组同步变更问题排查
问题描述
从父数组中提取子数组后,为子数组元素添加新键值对时,父数组也随之被修改。以下是相关代码及输出:
代码示例
const array = [{"integrationTypeId":1,"type":"aptus","fields":[{"name":"base_url"},{"name":"hash_key"}]}] function runArray(){ let connectorObject= {} let newArray if (array.length > 0) { connectorObject = array.find( (c) => c.integrationTypeId === 1, ); newArray = connectorObject.fields newArray !== undefined && newArray.map((object) => { return Object.assign(object, { inputType: "textField", value: object.name, }) }) } console.log(JSON.stringify(newArray)) console.log(JSON.stringify(array)) } runArray();
输出结果
Console 1. : newArray
[{"name":"base_url","inputType":"textField","value":"base_url"},{"name":"hash_key","inputType":"textField","value":"hash_key"}]
Console 2. : Parent Array
[{"integrationTypeId":1,"type":"aptus","fields":[{"name":"base_url","inputType":"textField","value":"base_url"},{"name":"hash_key","inputType":"textField","value":"hash_key"}]}]
尝试使用newArray.map(obj => ({ ...obj, inputType: "textField"}))也未解决该问题。
原因分析
JavaScript里,对象和数组都是引用类型。当你执行newArray = connectorObject.fields时,并没有复制出一个新的子数组,只是让newArray指向了父数组中fields数组的内存地址。后续用Object.assign直接修改数组内的对象时,本质是在修改原内存地址里的对象,父数组自然会同步变化。
至于你尝试的map方法没生效,是因为你只调用了map但没把返回的新数组赋值给newArray。map本身会返回包含新对象的新数组(通过扩展运算符...创建),但如果不把这个结果存起来,newArray仍然指向原来的数组,原对象还是会被修改。
解决方法
要避免修改父数组,需要创建子数组及其中对象的浅拷贝(如果对象没有嵌套结构,浅拷贝足够)。
方法1:用map直接生成新数组和新对象
把map的返回值赋值给newArray,同时通过扩展运算符创建新对象,不改动原对象:
const array = [{"integrationTypeId":1,"type":"aptus","fields":[{"name":"base_url"},{"name":"hash_key"}]}] function runArray(){ let connectorObject= {} let newArray if (array.length > 0) { connectorObject = array.find((c) => c.integrationTypeId === 1); // 生成包含新对象的新数组 newArray = connectorObject.fields.map((object) => ({ ...object, inputType: "textField", value: object.name })); } console.log(JSON.stringify(newArray)) console.log(JSON.stringify(array)) } runArray();
方法2:先拷贝数组再替换对象
如果需要保留原数组的操作逻辑,可以先拷贝数组,再逐个替换为新对象:
const array = [{"integrationTypeId":1,"type":"aptus","fields":[{"name":"base_url"},{"name":"hash_key"}]}] function runArray(){ let connectorObject= {} let newArray if (array.length > 0) { connectorObject = array.find((c) => c.integrationTypeId === 1); // 先拷贝原fields数组 newArray = [...connectorObject.fields]; // 遍历新数组,替换为带新属性的新对象 newArray = newArray.map((object) => ({ ...object, inputType: "textField", value: object.name })); } console.log(JSON.stringify(newArray)) console.log(JSON.stringify(array)) } runArray();
注意事项
如果你的数组对象存在多层嵌套(比如fields里的对象还有子对象),需要用深拷贝,比如JSON.parse(JSON.stringify(connectorObject.fields)),或者用Lodash的_.cloneDeep工具函数。
内容的提问来源于stack exchange,提问作者user2028

