克隆数组影响反应状态

时间:2020-06-20 20:58:57

标签: arrays reactjs ecmascript-6 spread-syntax use-context

我的React应用程序中的克隆数组出现问题。

我将包含数据的数组导入到名为ApplicationsData的应用程序中。这是一个对象数组。

import ApplicationsData from '../Store/Applications';

应用程序组件具有以下状态:

constructor(props) {
    super(props);    
    this.state = { 
        isOn: true,
        untouchedApplications: [...ApplicationsData],
        applications: [...ApplicationsData],
        activeWindows: [...defaultActive],
        background: "#3a6ea5"
    };
}

我使用“ ...”传播运算符对数据进行数组克隆。

在我的上下文中,我有一个方法-openApp-可以更改state.applications中某个元素的属性。

openApp: (appID) => {
                let appIndex = this.state.applications.findIndex( el => el.id == appID);
                let clone = [...ApplicationsData];
                
                clone[appIndex].isActive = true;
                clone[appIndex].newProp = true;
                clone[appIndex].isMinimized = false;
                this.setState({applications: clone})
            },

每当我进入clone[appIndex].prop = value时,无论state.applications和untouchedApplications都被该数据覆盖,尽管在state和openApp中都使用了分散运算符。状态为untouchedApplications的数组不会在应用程序或方法中的任何位置使用,但也会得到更新。另外,分配给克隆数组的newProp属性在具有数据的原始数组中不存在,但是在将其添加到克隆数组后的状态下确实会应用于两个数组。

我还尝试使用Array.fromstate.applications.slice()创建克隆。我在这里感到有点迷茫,因为我确定这是克隆数组的正确方法。

对不起,我的代码缩进很乱。我不知道如何正确设置SO格式。第一行位于其他行之前,或者所有行均始于8个制表符。对此也有任何提示。

2 个答案:

答案 0 :(得分:1)

您应该使用map函数

(appID) => {
   let clone = this.state.applications.map(item => {
        if (item.id == appId) {
            return {
                ...item,
                isActive: true,
                newProp: true,
                isMinimized: false
            }
        }
        return item;
    })    
    this.setState({applications: clone})
},

之所以会发生这种情况,是因为当您使用...运算符时,它会创建数组的浅表副本而不是深表副本。

let val = [{key: "value"}, {key: "value"}, {key: "value"}]
let copy = [...val];


let isEqualShallow = val[0] == copy[0]

console.log(isEqualShallow)

因此您可以看到副本中的第一个对象与另一个对象相等。但是,如果在内部对象上也使用散布运算符,则会得到一个深层副本。

    let val = [{key: "value"}, {key: "value"}, {key: "value"}]
    let copy = val.map(item => {
      return {...item}
    })


    let isEqualDeep = val[0] == copy[0]

    console.log(isEqualDeep)

答案 1 :(得分:1)

您要使用的数组是对象数组。对象是引用类型,这意味着,实际上,您的数组包含对对象的引用

当您将数组扩展到新数组中时,您不是在克隆对象,而只是在克隆它们的引用,因此,当您编写clone[appIndex]时,您是在引用两个数组中包含的同一对象

要解决此问题,可以使用map方法克隆要更改的对象:

const newArray = ApplicationsData.map(
  (item, index) =>
    index == appIndex ?
      ({
         ...item,
         isActive: true,
         newProp: true,
         isMinimized: false
       }) : 
    item
);

这将返回所有项目的原始对象引用,但您要更改的特定项目除外。在这种情况下,它将创建一个全新的对象并返回该引用,因此原始对象将保持不变。