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 ponent -- 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?
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 ponent -- 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?
Share Improve this question asked Jan 11, 2017 at 3:48 Christopher RonningChristopher Ronning 1,8903 gold badges19 silver badges28 bronze badges 5-
2
This may be the result of Object.assign only doing a shallow copy. Object.asssign reference. 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 Commented Jan 11, 2017 at 4:20 - This actually solved the problem. If you format this as an answer, I'll gladly accept it as the correct answer. – Christopher Ronning Commented Jan 11, 2017 at 4:24
-
1
So I guess the properties of
stuff
in my Parent ponent are actually references to properties of the actualstuff
prop. Is that a correct analysis, @BrandonRoberts ? – Christopher Ronning Commented Jan 11, 2017 at 4:25 - Why, though, is this same thing not applying to my Child ponent? – Christopher Ronning Commented Jan 11, 2017 at 4:28
-
If strict mode is enabled in App.js then may be instead of
const
uselet
i.e.const stuff = { thing1
-> tolet stuff = { thing1
– Muhammad Shahzad Commented Oct 3, 2020 at 8:52
3 Answers
Reset to default 10Object.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.
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))
Try changing const
to let
for your local copies