7

Let's say I have two Components. The parent is passed an Object as a property, which it then copies into local data store. It has a function to update this local store, which gets passed down to a child. Here is my parent Component:

const Parent = ({stuff}) => {
  const store = {
    stuff: Object.assign({}, stuff);
  }

  const updateStuff = (thing, property) => store.stuff[thing].property = thing;

  return <Child stuff={stuff} updateStuff={updateStuff} />
}

The Child Component has a similar structure -- it makes a copy of stuff, and mutates its copy of stuff on an <input>'s onChange. It then passes its own updated copy of stuff to the updateStuff function it received, in order to mutate the Parent's copy of the prop. Here is the Child.

const Child = ({stuff, updateStuff}) => {
  const stuff = {
    thing1: Object.assign({}, stuff.thing1),
    thing2: Object.assign({}, stuff.thing2)
  }

  const setProp = event => {
    const update = event.target.value;

    stuff.thing1.prop = update;
    updateStuff(thing1, stuff.thing1.prop)
  }

  return (
    <div>
      <input id="thing1" onChange={setProp} />
      <input id="thing1" onChange={setProp} />
    </div>
  )
}

Notice, I've used Object.assign to basically clone the stuff prop or its child properties, as the case necessitates. The reason for this: a React prop is read-only, and thus I need to create a clone to make changes on before passing it back to mutate the app's state (not shown here)/

Now, this works on the Child component -- setProp mutates the correct property of stuff, confirmed by logging to the console. However, when the method gets to updateTeam, I get an error message : Uncaught TypeError: Cannot assign to read only property 'side' of object '#<Object>'

Yet, both Components use the same principle: I am not mutating the prop, but rather I am mutating a locally-stored clone of the prop. Why does this work for Child, but not for Parent?

Christopher Ronning
  • 1,875
  • 3
  • 19
  • 28
  • 2
    This may be the result of Object.assign only doing a shallow copy. [Object.asssign reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Deep_Clone). Could you try deep cloning the objects anywhere you do Object.assign(). Something like`newStuff: JSON.parse(JSON.stringify(stuff))`. My thought is that the lower level parts of the object are not really a copy. – Special Character Jan 11 '17 at 04:20
  • This actually solved the problem. If you format this as an answer, I'll gladly accept it as the correct answer. – Christopher Ronning Jan 11 '17 at 04:24
  • 1
    So I guess the properties of `stuff` in my Parent component are actually references to properties of the actual `stuff` prop. Is that a correct analysis, @BrandonRoberts ? – Christopher Ronning Jan 11 '17 at 04:25
  • Why, though, is this same thing not applying to my Child component? – Christopher Ronning Jan 11 '17 at 04:28
  • If strict mode is enabled in App.js then may be instead of ```const``` use ```let``` i.e. ```const stuff = { thing1 ``` -> to ```let stuff = { thing1``` – Muhammad Shahzad Oct 03 '20 at 08:52

3 Answers3

10

Object.assign only does a shallow copy of the prop Object.assign Reference. In order to make a true deep copy of the prop (and get rid of the error) you can do a deep copy with newStuff: JSON.parse(JSON.stringify(stuff)). Glad this helped!

The real reason behind this is given in an example:

let original = {
    name: 'Test',
    nestedObj: {
        (...some properties)
    }
}

In the example above, the original object property 'name' is a new copy but the nested object is still a reference to the original. This way when you try and edit the part of the nested object it references the original and yells that it is immutable.

Special Character
  • 2,321
  • 4
  • 25
  • 34
  • Many thanks... I still am unsure as to why this same thing didn't happen with Child -- considering that I am also mutating properties (of properties) of `thing`. Anyways, thank you. – Christopher Ronning Jan 11 '17 at 04:32
  • 1
    Here is a good answer to deep copy vs. shallow copy : http://stackoverflow.com/questions/184710/what-is-the-difference-between-a-deep-copy-and-a-shallow-copy. This was really helpful to me when I ran across similar issues in react. – Special Character Jan 11 '17 at 04:36
  • 1
    You sir are a high-quality user. – Christopher Ronning Jan 11 '17 at 04:37
1

I stumbled across this question and decided it needs an updated answer. We can use structuredClone(stuff) instead of the older method of JSON.parse(JSON.stringify(stuff))

-1

Try changing const to let for your local copies