0

I'm having issues with cloning arrays in my react application.

I import an array with data to my app called ApplicationsData. It's an array of objects.

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

App component has the following state:

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

I make a clone of the array with data using the "..." spread operator.

In my context I have a method - openApp - Which changes a property in an element in 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})
            },

Whenever I get to the clone[appIndex].prop = value both state.applications and untouchedApplications gets overridden with that data, despite having used spread operators both in state and in openApp. The untouchedApplications array in state is not used anywhere in the app or the method but also gets updated. Also the newProp property being assigned to the cloned array is not present in the original array with data but does get applied to both arrays in the state after adding it to the cloned array.

I have also tried using Array.from and state.applications.slice() to create clones. I'm feeling a bit lost here, since I was sure this was the correct way of cloning arrays.

I'm sorry for messy code indentation. I have no clue how to make SO format it properly. Either first line is way before other lines or all lines start 8 tabs in. Any tips on that are appreciated as well.

4

2 回答 2

1

you should use map function

(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})
},

This happens because when you use the ... operator it creates a shallow copy of the array and not a deep copy.

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


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

console.log(isEqualShallow)

so you can see how that first object in the copy is equal to the other object. But if you use spread operators on the internal objects as well then you get a deep copy.

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


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

    console.log(isEqualDeep)

于 2020-06-20T21:03:45.977 回答
1

The arrays you are working with are arrays of objects. Objects are reference types, which means that really, your arrays contain references to the objects.

When you spread the array into a new array, you are not cloning the objects, just their references, therefore when you write clone[appIndex], you are referring to the same object that is contained in both arrays.

To get around this, you can use the map method to clone the object that you want to change:

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

This will return the original object reference for all items, except for the specific item that you want to change. In that case, it creates a brand new object and returns that reference, so the original object is left untouched.

于 2020-06-20T21:15:51.417 回答