The local copy of React prop is read-only

advertisements

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?


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.