1

I am using React.

I have an array of objects in my state.

this.state = {
  team: [{
    name:'Bob',
    number:23
  },
  {
    name:'Jim',
    number:43
  }]
}

When I try to make a copy of the array to change a object's property, I don't get the results I want.

I have tried:

let tempTeam = [...this.state.team]

Any change to tempTeam also mutates this.state.team

AND

let tempTeam = this.state.team.map(player => return {...player})

This just throws an error, it doesn't like {...player}

How can I get the array of objects without it referencing this.state.team?

stefan.kenyon
  • 319
  • 1
  • 5
  • 16
  • What error does it throw? I can see a syntax error as you have `}}` instead of `})` at the end of the line – Anthony Sep 16 '18 at 00:48
  • Possible duplicate of [How to clone a Javascript Array of Objects?](https://stackoverflow.com/questions/42523881/how-to-clone-a-javascript-array-of-objects) – Andy Taton Sep 16 '18 at 00:52

3 Answers3

4

You have a syntax error in the posted code. If you change it to:

let tempTeam = this.state.team.map(player => ({...player}));

Or:

let tempTeam = this.state.team.map(player => { 
                 return { ...player };
               });

You will get a new array with the same object values without the references.

Anthony
  • 6,422
  • 2
  • 17
  • 34
2

Object.assign and spread syntax create shallow copies. If you mutate a nested property in the copied one you also mutate original object.

Most of the time we use map, filter, slice to get a new array by using the original one. But, even using those methods, we should not mutate the properties directly, we should return again new ones using Object.assign or spread syntax again.

As explained you have a syntax error in your code, if you fix it you can get the new array. But, most of the time you will do operations like these:

const state = {
  team: [{
    name:'Bob',
    number:23
  },
  {
    name:'Jim',
    number:43
  }]
};

const changeAll = state.team.map( player => ({
  ...player, number: player.number + 1, 
}));

// or

const playerTheOne = state.team.filter(player => player.number === 23);
const notPlayerTheOne = state.team.filter(player => player.number !== 23);

// or

const changeJustOne = state.team.map( player => {
  if ( player.number === 23 ) {
    return { ...player, name: "Joe" };
  }
  return player;
});
console.log( state.team );
console.log( changeAll );
console.log( playerTheOne );
console.log( notPlayerTheOne );
console.log( changeJustOne );

As you can see, you don't create a new array then mutate it, you are mutating it while you are creating it.

devserkan
  • 16,870
  • 4
  • 31
  • 47
  • What do you mean by this: "If you mutate a nested property in the copied one you also mutate original object." Spreading and mapping like he does then modifying the copy will *not* change the original. – CodeDraken Sep 16 '18 at 01:15
  • My first sentence is about `Object.assign` and spread syntax. After mapping (even without using spread or `Object.assign`) of course does not mutate the original one. – devserkan Sep 16 '18 at 01:17
  • 1
    If you map without spreading it *will* mutate the original one... I think you have everything backwards lol – CodeDraken Sep 16 '18 at 01:19
  • You are damn right here! Thank you, I've updated my poor knowledge and also my answer :) I did not know that even after mapping it uses the same reference. I always think that it is a totally new array after mapping it. – devserkan Sep 16 '18 at 01:26
  • 1
    Happy to help :D I also needed to make sure I'm not just crazy. – CodeDraken Sep 16 '18 at 01:48
  • @CodeDraken, definitely you are not :) I'm the confused one here. I never mutate the arrays or objects without returning new items, this is why I am surprised. But, I should have thought that if we return the item itself it will use the same reference as the original one. It is very clear to me now. – devserkan Sep 16 '18 at 02:02
0

Using Array.slice() creates a clone, so this should should work:

let tempTeam = this.state.team.slice();
  • 1
    From [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice): "For object references (and not the actual object), `slice` copies object references into the new array. Both the original and new array refer to the same object." So there will still be references in this case. – Andy Taton Sep 16 '18 at 00:49