slanden
slanden

Reputation: 1347

Object.assign() and Spread properties still mutating original

I'm trying to assign the value of an array element to an object. After first attempting something like, e.g.bar = foo[0]; I've discovered that any change to bar also changes foo[0], due to having the same reference.

Awesome, thought no one, and upon reading up on immutability and the ES6 Object.assign() method and spread properties, I thought it would fix the issue. However, in this case it doesn't. What am I missing?

EDIT: Sorry about the accountTypes confusion, I fixed the example. Also, I would like to keep the class structure of Settings, so let copy = JSON.parse(JSON.stringify(original)); is not really what I'm after in this case.

//this object will change according to a selection
currentPreset;

//this should remain unchanged
presets: {name: string, settings: Settings}[] = [];


  ngOnInit()
  {
    this.currentPreset = {
      name: '',
      settings: new Settings()
    }

    this.presets.push({name: 'Preset1', settings: new Settings({
        settingOne: 'foo',
        settingTwo: false,
        settingThree: 14
      })
    });

  }

 /**
  * Select an item from the `presets` array and assign it, 
  * by value(not reference), to `currentPreset`.
  * 
  * @Usage In an HTML form, a <select> element's `change` event calls 
  * this method to fill the form's controls with the values of a
  * selected item from the `presets` array. Subsequent calls to this
  * method should not affect the value of the `presets` array.
  *
  * @param value - Expects a numerical index or the string 'new'
  */
  setPreset(value)
  {
    if(value == 'new')
    {
      this.currentPreset.name = '';
      this.currentPreset.settings.reset();
    }
    else
    {
      this.currentPreset = {...this.presets[value]};
      //same as above
      //this.currentPreset = Object.assign({}, this.presets[value]);
    }
  }

Upvotes: 3

Views: 2581

Answers (4)

JrmDel
JrmDel

Reputation: 462

I had the same problem recently, and I could not figure out why some of my objects were changing their properties. I had to change my code to avoid mutation. Some of the answers here helped me understand afterwards, such as this great article : https://alistapart.com/article/why-mutation-can-be-scary/

I recommend it. The author gives a lot of examples and useful libraries that can outperform Object.assign() when it comes to embedded properties.

Upvotes: 0

hakuna
hakuna

Reputation: 6701

Try this : let copy = original.map(item => Object.assign({}, ...item)); This will create a new object without any reference to the old object original

In case if you want to do this for an array try the same with []

let copy = original.map(item => Object.assign([], ...item));

Upvotes: 4

slanden
slanden

Reputation: 1347

This doesn't really answer the question, but since the object I'm trying not to mutate doesn't have nested properties within, I call the assignment at the property level and the shallow copy here is fine.

setPreset(value)
  {
    if(value == 'new')
    {
      this.currentPreset.name = '';
      this.currentPreset.settings.reset();
    }
    else
    {
      this.currentPreset.name = this.presets[value].name;
      this.currentPreset.privileges = Object.assign(new Settings(), 
                                         this.presets[value].settings);
    }
  }

A better solution, since I'm creating a new Settings() anyway, might be to move this logic to a Settings class method and call it in the constructor

Upvotes: 0

Luis Gonzalez
Luis Gonzalez

Reputation: 538

You have to do a deep copy, this the easiest way:

let copy = JSON.parse(JSON.stringify(original));

Upvotes: 1

Related Questions