How to update nested state properties in React
up vote
115
down vote
favorite
I'm trying to organize my state by using nested property like this:
this.state = {
someProperty: {
flag:true
}
}
But updating state like this,
this.setState({ someProperty.flag: false });
doesn't work. How can this be done correctly?
javascript reactjs setstate
add a comment |
up vote
115
down vote
favorite
I'm trying to organize my state by using nested property like this:
this.state = {
someProperty: {
flag:true
}
}
But updating state like this,
this.setState({ someProperty.flag: false });
doesn't work. How can this be done correctly?
javascript reactjs setstate
2
What do you mean doesn't work? This is not a very good question - what happened? Any errors? What errors?
– Darren Sweeney
Mar 27 '17 at 7:53
Try to read: stackoverflow.com/questions/18933985/…
– Andrew Paramoshkin
Mar 27 '17 at 7:58
4
The answer is Don't use Nested State in React. Read this excellent answer.
– Qwerty
Aug 10 at 8:57
nested state is bad programming style in React JS
– Troyan Victor
Oct 5 at 8:30
add a comment |
up vote
115
down vote
favorite
up vote
115
down vote
favorite
I'm trying to organize my state by using nested property like this:
this.state = {
someProperty: {
flag:true
}
}
But updating state like this,
this.setState({ someProperty.flag: false });
doesn't work. How can this be done correctly?
javascript reactjs setstate
I'm trying to organize my state by using nested property like this:
this.state = {
someProperty: {
flag:true
}
}
But updating state like this,
this.setState({ someProperty.flag: false });
doesn't work. How can this be done correctly?
javascript reactjs setstate
javascript reactjs setstate
edited Jul 30 at 0:29
Dan Dascalescu
64.3k20195270
64.3k20195270
asked Mar 27 '17 at 7:51
Alex Yong
9202719
9202719
2
What do you mean doesn't work? This is not a very good question - what happened? Any errors? What errors?
– Darren Sweeney
Mar 27 '17 at 7:53
Try to read: stackoverflow.com/questions/18933985/…
– Andrew Paramoshkin
Mar 27 '17 at 7:58
4
The answer is Don't use Nested State in React. Read this excellent answer.
– Qwerty
Aug 10 at 8:57
nested state is bad programming style in React JS
– Troyan Victor
Oct 5 at 8:30
add a comment |
2
What do you mean doesn't work? This is not a very good question - what happened? Any errors? What errors?
– Darren Sweeney
Mar 27 '17 at 7:53
Try to read: stackoverflow.com/questions/18933985/…
– Andrew Paramoshkin
Mar 27 '17 at 7:58
4
The answer is Don't use Nested State in React. Read this excellent answer.
– Qwerty
Aug 10 at 8:57
nested state is bad programming style in React JS
– Troyan Victor
Oct 5 at 8:30
2
2
What do you mean doesn't work? This is not a very good question - what happened? Any errors? What errors?
– Darren Sweeney
Mar 27 '17 at 7:53
What do you mean doesn't work? This is not a very good question - what happened? Any errors? What errors?
– Darren Sweeney
Mar 27 '17 at 7:53
Try to read: stackoverflow.com/questions/18933985/…
– Andrew Paramoshkin
Mar 27 '17 at 7:58
Try to read: stackoverflow.com/questions/18933985/…
– Andrew Paramoshkin
Mar 27 '17 at 7:58
4
4
The answer is Don't use Nested State in React. Read this excellent answer.
– Qwerty
Aug 10 at 8:57
The answer is Don't use Nested State in React. Read this excellent answer.
– Qwerty
Aug 10 at 8:57
nested state is bad programming style in React JS
– Troyan Victor
Oct 5 at 8:30
nested state is bad programming style in React JS
– Troyan Victor
Oct 5 at 8:30
add a comment |
16 Answers
16
active
oldest
votes
up vote
164
down vote
accepted
In order to setState
for a nested object you can follow the below approach as I think setState doesn't handle nested updates.
var someProperty = {...this.state.someProperty}
someProperty.flag = true;
this.setState({someProperty})
The idea is to create a dummy object perform operations on it and then replace the component's state with the updated object
Now, the spread operator creates only one level nested copy of the object. If your state is highly nested like:
this.state = {
someProperty: {
someOtherProperty: {
anotherProperty: {
flag: true
}
..
}
...
}
...
}
You could setState using spread operator at each level like
this.setState(prevState => ({
...prevState,
someProperty: {
...prevState.someProperty,
someOtherProperty: {
...prevState.someProperty.someOtherProperty,
anotherProperty: {
...prevState.someProperty.someOtherProperty.anotherProperty,
flag: false
}
}
}
}))
However the above syntax get every ugly as the state becomes more and more nested and hence I recommend you to use immutability-helper
package to update the state.
See this answer on how to update state with immutability helper
.
1
@Ohgodwhy , no this is not accessing the state directly since {...this.state.someProperty} retuns a new obejct tosomeProperty
and we are modifying that
– Shubham Khatri
Apr 15 '17 at 9:17
2
this didn't work for me.. i'm using react version @15.6.1
– Rajiv
Jun 30 '17 at 20:36
2
@Stophface We can use Lodash to deep clone sure, but not everyone would include this library just to setState
– Shubham Khatri
Nov 23 '17 at 5:36
3
Doesn't this violate reactjs.org/docs/… ? If two changes to the nested object got batched the last change would overwrite the first. So the most correct way would be:this.setState((prevState) => ({nested: {...prevState.nested, propertyToSet: newValue}})
A bit tedious though...
– olejorgenb
Dec 21 '17 at 20:55
1
I don't think you need the...prevState,
in the last example, you onlysomeProperty
and its children
– Jemar Jones
May 25 at 19:43
|
show 7 more comments
up vote
57
down vote
To write it in one line
this.setState({ someProperty: { ...this.state.someProperty, flag: false} });
3
It is recommended to use the updater for setState instead of directly accessing this.state within setState. reactjs.org/docs/react-component.html#setstate
– Raghu Teja
May 23 at 12:52
I believe that's saying, don't readthis.state
immediately aftersetState
and expect it to have the new value. Versus callingthis.state
withinsetState
. Or is there also an issue with the latter that isn't clear to me?
– trpt4him
Jun 1 at 13:06
As trpt4him said, the link you gave talks about the problem of accessingthis.state
aftersetState
. Besides, we are not passingthis.state
tosetState
. The…
operator spread properties. It is the same as Object.assign(). github.com/tc39/proposal-object-rest-spread
– Yoseph
Jun 4 at 4:27
add a comment |
up vote
23
down vote
Sometimes direct answers are not the best ones :)
Short version:
this code
this.state = {
someProperty: {
flag: true
}
}
should be simplified as something like
this.state = {
somePropertyFlag: true
}
Long version:
Currently you shouldn't want to work with nested state in React. Because React is not oriented to work with nested states and all solutions proposed here look as hacks. They don't use the framework but fight with it. They suggest to write not so clear code for doubtful purpose of grouping some properties. So they are very interesting as an answer to the challenge but practically useless.
Lets imagine the following state:
{
parent: {
child1: 'value 1',
child2: 'value 2',
...
child100: 'value 100'
}
}
What will happen if you change just a value of child1
? React will not re-render the view because it uses shallow comparison and it will find that parent
property didn't change. BTW mutating the state object directly is considered to be a bad practice in general.
So you need to re-create the whole parent
object. But in this case we will meet another problem. React will think that all children have changed their values and will re-render all of them. Of course it is not good for performance.
It is still possible to solve that problem by writing some complicated logic in shouldComponentUpdate()
but I would prefer to stop here and use simple solution from the short version.
I think there are use cases where a nested state is perfectly fine. E.g. a state dynamically keeping track of key-value pairs. See here how you can update the state for just a single entry in the state: reactjs.org/docs/…
– pors
Aug 30 at 13:45
Shallow comparison is used for pure components by default.
– Basel Shishani
Sep 26 at 12:10
add a comment |
up vote
18
down vote
If you are using ES2015 you have access to the Object.assign. You can use it as follows to update a nested object.
this.setState({
someProperty: Object.assign({}, this.state.someProperty, {flag: false})
});
You merge the updated properties with the existing and use the returned object to update the state.
Edit: Added an empty object as target to the assign function to make sure the state isn't mutated directly as carkod pointed out.
8
And I could be wrong but I don't think you're using React without ES2015.
– Madbreaks
Nov 10 '17 at 18:29
add a comment |
up vote
12
down vote
Disclaimer
Nested State in React is wrong design
Read this excellent answer.
Reasoning behind this answer:
React's setState is just a built-in convenience, but you soon realise
that it has its limits. Using custom properties and intelligent use of
forceUpdate gives you much more.
eg:
class MyClass extends React.Component {
myState = someObject
inputValue = 42
...
MobX, for example, ditches state completely and uses custom observable properties.
Use Observables instead of state in React components.
the best answer of all - see example here
There is another shorter way to update whatever nested property.
this.setState(state => {
state.nested.flag = false
state.another.deep.prop = true
return state
})
On one line
this.setState(state => (state.nested.flag = false, state))
Of course this is abusing some core principles, as the state
should be read-only, but since you are immediately discarding the old state and replacing it with new state, it is completely ok.
Warning
Even though the component containing the state will update and rerender properly (except this gotcha), the props will fail to propagate to children (see Spymaster's comment below). Only use this technique if you know what you are doing.
For example, you may pass a changed flat prop that is updated and passed easily.
render(
//some complex render with your nested state
<ChildComponent complexNestedProp={this.state.nested} pleaseRerender={Math.random()}/>
)
Now even though reference for complexNestedProp did not change (shouldComponentUpdate)
this.props.complexNestedProp === nextProps.complexNestedProp
the component will rerender whenever parent component updates, which is the case after calling this.setState
or this.forceUpdate
in the parent.
6
You shouldn't be mutating any react state anywhere. If there are nested components that use data from state.nested or state.another, they won't re-render nor will their children. This is because the object did not change, thus React thinks nothing has changed.
– SpyMaster356
May 12 at 16:51
@SpyMaster356 I have updated my answer to address this. Also note that MobX, for example, ditchesstate
completely and uses custom observable properties. React'ssetState
is just a built-in convenience, but you soon realise that it has its limits. Using custom properties and intelligent use offorceUpdate
gives you much more. Use Observables instead of state in React components.
– Qwerty
Aug 9 at 9:44
Also I added an example react-experiments.herokuapp.com/state-flow (link to git at the top)
– Qwerty
Aug 9 at 16:28
add a comment |
up vote
6
down vote
There are many libraries to help with this. For example, using immutability-helper:
import update from 'immutability-helper';
const newState = update(this.state, {
someProperty: {flag: {$set: false}},
};
this.setState(newState);
Or using lodash/fp:
import {merge} from 'lodash/fp';
const newState = merge(this.state, {
someProperty: {flag: false},
});
If you uselodash
, you probably want to try_.cloneDeep
and set state to be the cloned copy.
– Yixing Liu
Jul 26 at 20:37
@YixingLiu: I've updated the answer to uselodash/fp
, so we don't have to worry about mutations.
– tokland
Jul 26 at 20:50
add a comment |
up vote
5
down vote
Here's a variation on the first answer given in this thread which doesn't require any extra packages, libraries or special functions.
state = {
someProperty: {
flag: 'string'
}
}
handleChange = (value) => {
const newState = {...this.state.someProperty, flag: value}
this.setState({ someProperty: newState })
}
In order to set the state of a specific nested field, you have set the whole object. I did this by creating a variable, newState
and spreading the contents of the current state into it first using the ES2015 spread operator. Then, I replaced the value of this.state.flag
with the new value (since I set flag: value
after I spread the current state into the object, the flag
field in the current state is overridden). Then, I simply set the state of someProperty
to my newState
object.
add a comment |
up vote
5
down vote
You can also go this way (which feels more readable to me):
const newState = Object.assign({}, this.state);
newState.property.nestedProperty = 'newValue';
this.setState(newState);
add a comment |
up vote
2
down vote
I used this solution.
If you have a nested state like this:
this.state = {
formInputs:{
friendName:{
value:'',
isValid:false,
errorMsg:''
},
friendEmail:{
value:'',
isValid:false,
errorMsg:''
}
}
you can declare the handleChange function that copy current status and re-assigns it with changed values
handleChange(el) {
let inputName = el.target.name;
let inputValue = el.target.value;
let statusCopy = Object.assign({}, this.state);
statusCopy.formInputs[inputName].value = inputValue;
this.setState(statusCopy);
}
here the html with the event listener
<input type="text" onChange={this.handleChange} " name="friendName" />
add a comment |
up vote
2
down vote
Two other options not mentioned yet:
- If you have deeply nested state, consider if you can restructure the child objects to sit at the root. This makes the data easier to update.
- There are many handy libraries available for handling immutable state listed in the Redux docs. I recommend Immer since it allows you to write code in a mutative manner but handles the necessary cloning behind the scenes. It also freezes the resulting object so you can't accidentally mutate it later.
add a comment |
up vote
1
down vote
To make things generic, I worked on @ShubhamKhatri's and @Qwerty's answers.
state object
this.state = {
name: '',
grandParent: {
parent1: {
child: ''
},
parent2: {
child: ''
}
}
};
input controls
<input
value={this.state.name}
onChange={this.updateState}
type="text"
name="name"
/>
<input
value={this.state.grandParent.parent1.child}
onChange={this.updateState}
type="text"
name="grandParent.parent1.child"
/>
<input
value={this.state.grandParent.parent2.child}
onChange={this.updateState}
type="text"
name="grandParent.parent2.child"
/>
updateState method
setState as @ShubhamKhatri's answer
updateState(event) {
const path = event.target.name.split('.');
const depth = path.length;
const oldstate = this.state;
const newstate = { ...oldstate };
let newStateLevel = newstate;
let oldStateLevel = oldstate;
for (let i = 0; i < depth; i += 1) {
if (i === depth - 1) {
newStateLevel[path[i]] = event.target.value;
} else {
newStateLevel[path[i]] = { ...oldStateLevel[path[i]] };
oldStateLevel = oldStateLevel[path[i]];
newStateLevel = newStateLevel[path[i]];
}
}
this.setState(newstate);
}
setState as @Qwerty's answer
updateState(event) {
const path = event.target.name.split('.');
const depth = path.length;
const state = { ...this.state };
let ref = state;
for (let i = 0; i < depth; i += 1) {
if (i === depth - 1) {
ref[path[i]] = event.target.value;
} else {
ref = ref[path[i]];
}
}
this.setState(state);
}
Note: These above methods won't work for arrays
add a comment |
up vote
1
down vote
We use Immer https://github.com/mweststrate/immer to handle these kinds of issues.
Just replaced this code in one of our components
this.setState(prevState => ({
...prevState,
preferences: {
...prevState.preferences,
[key]: newValue
}
}));
With this
import produce from 'immer';
this.setState(produce(draft => {
draft.preferences[key] = newValue;
}));
With immer you handle your state as a "normal object".
The magic happens behind the scene with proxy objects.
add a comment |
up vote
1
down vote
Create a copy of the state:let someProperty = JSON.parse(JSON.stringify(this.state.someProperty))
make changes in this object:someProperty.flag = "false"
now update the state
this.setState({someProperty})
add a comment |
up vote
0
down vote
I found this to work for me, having a project form in my case where for example you have an id, and a name and I'd rather maintain state for a nested project.
return (
<div>
<h2>Project Details</h2>
<form>
<Input label="ID" group type="number" value={this.state.project.id} onChange={(event) => this.setState({ project: {...this.state.project, id: event.target.value}})} />
<Input label="Name" group type="text" value={this.state.project.name} onChange={(event) => this.setState({ project: {...this.state.project, name: event.target.value}})} />
</form>
</div>
)
Let me know!
add a comment |
up vote
0
down vote
Something like this might suffice,
const isObject = (thing) => {
if(thing &&
typeof thing === 'object' &&
typeof thing !== null
&& !(Array.isArray(thing))
){
return true;
}
return false;
}
/*
Call with an array containing the path to the property you want to access
And the current component/redux state.
For example if we want to update `hello` within the following obj
const obj = {
somePrimitive:false,
someNestedObj:{
hello:1
}
}
we would do :
//clone the object
const cloned = clone(['someNestedObj','hello'],obj)
//Set the new value
cloned.someNestedObj.hello = 5;
*/
const clone = (arr, state) => {
let clonedObj = {...state}
const originalObj = clonedObj;
arr.forEach(property => {
if(!(property in clonedObj)){
throw new Error('State missing property')
}
if(isObject(clonedObj[property])){
clonedObj[property] = {...originalObj[property]};
clonedObj = clonedObj[property];
}
})
return originalObj;
}
const nestedObj = {
someProperty:true,
someNestedObj:{
someOtherProperty:true
}
}
const clonedObj = clone(['someProperty'], nestedObj);
console.log(clonedObj === nestedObj) //returns false
console.log(clonedObj.someProperty === nestedObj.someProperty) //returns true
console.log(clonedObj.someNestedObj === nestedObj.someNestedObj) //returns true
console.log()
const clonedObj2 = clone(['someProperty','someNestedObj','someOtherProperty'], nestedObj);
console.log(clonedObj2 === nestedObj) // returns false
console.log(clonedObj2.someNestedObj === nestedObj.someNestedObj) //returns false
//returns true (doesn't attempt to clone because its primitive type)
console.log(clonedObj2.someNestedObj.someOtherProperty === nestedObj.someNestedObj.someOtherProperty)
add a comment |
up vote
0
down vote
I take very seriously the concerns already voiced around creating a complete copy of your component state. That being said, if compromised performance is not of paramount concern (i.e. you don't have a very deep tree of components which React would want to re-render) I would suggest Immer.
import produce from 'immer';
<Input
value={this.state.form.username}
onChange={e => produce(this.state, s => { s.form.username = e.target.value }) } />
This should work for React.PureComponent
(i.e. shallow state comparisons by React) as Immer
cleverly uses a proxy object to efficiently copy an arbitrarily deep state tree. Immer is also more typesafe compared to libraries like Immutability Helper, and is ideal for Javascript and Typescript users alike.
Typescript utility function
function setStateDeep<S>(comp: React.Component<any, S, any>, fn: (s:
Draft<Readonly<S>>) => any) {
comp.setState(produce(comp.state, s => { fn(s); }))
}
onChange={e => setStateDeep(this, s => s.form.username = e.target.value)}
add a comment |
16 Answers
16
active
oldest
votes
16 Answers
16
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
164
down vote
accepted
In order to setState
for a nested object you can follow the below approach as I think setState doesn't handle nested updates.
var someProperty = {...this.state.someProperty}
someProperty.flag = true;
this.setState({someProperty})
The idea is to create a dummy object perform operations on it and then replace the component's state with the updated object
Now, the spread operator creates only one level nested copy of the object. If your state is highly nested like:
this.state = {
someProperty: {
someOtherProperty: {
anotherProperty: {
flag: true
}
..
}
...
}
...
}
You could setState using spread operator at each level like
this.setState(prevState => ({
...prevState,
someProperty: {
...prevState.someProperty,
someOtherProperty: {
...prevState.someProperty.someOtherProperty,
anotherProperty: {
...prevState.someProperty.someOtherProperty.anotherProperty,
flag: false
}
}
}
}))
However the above syntax get every ugly as the state becomes more and more nested and hence I recommend you to use immutability-helper
package to update the state.
See this answer on how to update state with immutability helper
.
1
@Ohgodwhy , no this is not accessing the state directly since {...this.state.someProperty} retuns a new obejct tosomeProperty
and we are modifying that
– Shubham Khatri
Apr 15 '17 at 9:17
2
this didn't work for me.. i'm using react version @15.6.1
– Rajiv
Jun 30 '17 at 20:36
2
@Stophface We can use Lodash to deep clone sure, but not everyone would include this library just to setState
– Shubham Khatri
Nov 23 '17 at 5:36
3
Doesn't this violate reactjs.org/docs/… ? If two changes to the nested object got batched the last change would overwrite the first. So the most correct way would be:this.setState((prevState) => ({nested: {...prevState.nested, propertyToSet: newValue}})
A bit tedious though...
– olejorgenb
Dec 21 '17 at 20:55
1
I don't think you need the...prevState,
in the last example, you onlysomeProperty
and its children
– Jemar Jones
May 25 at 19:43
|
show 7 more comments
up vote
164
down vote
accepted
In order to setState
for a nested object you can follow the below approach as I think setState doesn't handle nested updates.
var someProperty = {...this.state.someProperty}
someProperty.flag = true;
this.setState({someProperty})
The idea is to create a dummy object perform operations on it and then replace the component's state with the updated object
Now, the spread operator creates only one level nested copy of the object. If your state is highly nested like:
this.state = {
someProperty: {
someOtherProperty: {
anotherProperty: {
flag: true
}
..
}
...
}
...
}
You could setState using spread operator at each level like
this.setState(prevState => ({
...prevState,
someProperty: {
...prevState.someProperty,
someOtherProperty: {
...prevState.someProperty.someOtherProperty,
anotherProperty: {
...prevState.someProperty.someOtherProperty.anotherProperty,
flag: false
}
}
}
}))
However the above syntax get every ugly as the state becomes more and more nested and hence I recommend you to use immutability-helper
package to update the state.
See this answer on how to update state with immutability helper
.
1
@Ohgodwhy , no this is not accessing the state directly since {...this.state.someProperty} retuns a new obejct tosomeProperty
and we are modifying that
– Shubham Khatri
Apr 15 '17 at 9:17
2
this didn't work for me.. i'm using react version @15.6.1
– Rajiv
Jun 30 '17 at 20:36
2
@Stophface We can use Lodash to deep clone sure, but not everyone would include this library just to setState
– Shubham Khatri
Nov 23 '17 at 5:36
3
Doesn't this violate reactjs.org/docs/… ? If two changes to the nested object got batched the last change would overwrite the first. So the most correct way would be:this.setState((prevState) => ({nested: {...prevState.nested, propertyToSet: newValue}})
A bit tedious though...
– olejorgenb
Dec 21 '17 at 20:55
1
I don't think you need the...prevState,
in the last example, you onlysomeProperty
and its children
– Jemar Jones
May 25 at 19:43
|
show 7 more comments
up vote
164
down vote
accepted
up vote
164
down vote
accepted
In order to setState
for a nested object you can follow the below approach as I think setState doesn't handle nested updates.
var someProperty = {...this.state.someProperty}
someProperty.flag = true;
this.setState({someProperty})
The idea is to create a dummy object perform operations on it and then replace the component's state with the updated object
Now, the spread operator creates only one level nested copy of the object. If your state is highly nested like:
this.state = {
someProperty: {
someOtherProperty: {
anotherProperty: {
flag: true
}
..
}
...
}
...
}
You could setState using spread operator at each level like
this.setState(prevState => ({
...prevState,
someProperty: {
...prevState.someProperty,
someOtherProperty: {
...prevState.someProperty.someOtherProperty,
anotherProperty: {
...prevState.someProperty.someOtherProperty.anotherProperty,
flag: false
}
}
}
}))
However the above syntax get every ugly as the state becomes more and more nested and hence I recommend you to use immutability-helper
package to update the state.
See this answer on how to update state with immutability helper
.
In order to setState
for a nested object you can follow the below approach as I think setState doesn't handle nested updates.
var someProperty = {...this.state.someProperty}
someProperty.flag = true;
this.setState({someProperty})
The idea is to create a dummy object perform operations on it and then replace the component's state with the updated object
Now, the spread operator creates only one level nested copy of the object. If your state is highly nested like:
this.state = {
someProperty: {
someOtherProperty: {
anotherProperty: {
flag: true
}
..
}
...
}
...
}
You could setState using spread operator at each level like
this.setState(prevState => ({
...prevState,
someProperty: {
...prevState.someProperty,
someOtherProperty: {
...prevState.someProperty.someOtherProperty,
anotherProperty: {
...prevState.someProperty.someOtherProperty.anotherProperty,
flag: false
}
}
}
}))
However the above syntax get every ugly as the state becomes more and more nested and hence I recommend you to use immutability-helper
package to update the state.
See this answer on how to update state with immutability helper
.
edited Apr 10 at 18:55
Bastiaan ten Klooster
13812
13812
answered Mar 27 '17 at 8:25
Shubham Khatri
75.1k1384123
75.1k1384123
1
@Ohgodwhy , no this is not accessing the state directly since {...this.state.someProperty} retuns a new obejct tosomeProperty
and we are modifying that
– Shubham Khatri
Apr 15 '17 at 9:17
2
this didn't work for me.. i'm using react version @15.6.1
– Rajiv
Jun 30 '17 at 20:36
2
@Stophface We can use Lodash to deep clone sure, but not everyone would include this library just to setState
– Shubham Khatri
Nov 23 '17 at 5:36
3
Doesn't this violate reactjs.org/docs/… ? If two changes to the nested object got batched the last change would overwrite the first. So the most correct way would be:this.setState((prevState) => ({nested: {...prevState.nested, propertyToSet: newValue}})
A bit tedious though...
– olejorgenb
Dec 21 '17 at 20:55
1
I don't think you need the...prevState,
in the last example, you onlysomeProperty
and its children
– Jemar Jones
May 25 at 19:43
|
show 7 more comments
1
@Ohgodwhy , no this is not accessing the state directly since {...this.state.someProperty} retuns a new obejct tosomeProperty
and we are modifying that
– Shubham Khatri
Apr 15 '17 at 9:17
2
this didn't work for me.. i'm using react version @15.6.1
– Rajiv
Jun 30 '17 at 20:36
2
@Stophface We can use Lodash to deep clone sure, but not everyone would include this library just to setState
– Shubham Khatri
Nov 23 '17 at 5:36
3
Doesn't this violate reactjs.org/docs/… ? If two changes to the nested object got batched the last change would overwrite the first. So the most correct way would be:this.setState((prevState) => ({nested: {...prevState.nested, propertyToSet: newValue}})
A bit tedious though...
– olejorgenb
Dec 21 '17 at 20:55
1
I don't think you need the...prevState,
in the last example, you onlysomeProperty
and its children
– Jemar Jones
May 25 at 19:43
1
1
@Ohgodwhy , no this is not accessing the state directly since {...this.state.someProperty} retuns a new obejct to
someProperty
and we are modifying that– Shubham Khatri
Apr 15 '17 at 9:17
@Ohgodwhy , no this is not accessing the state directly since {...this.state.someProperty} retuns a new obejct to
someProperty
and we are modifying that– Shubham Khatri
Apr 15 '17 at 9:17
2
2
this didn't work for me.. i'm using react version @15.6.1
– Rajiv
Jun 30 '17 at 20:36
this didn't work for me.. i'm using react version @15.6.1
– Rajiv
Jun 30 '17 at 20:36
2
2
@Stophface We can use Lodash to deep clone sure, but not everyone would include this library just to setState
– Shubham Khatri
Nov 23 '17 at 5:36
@Stophface We can use Lodash to deep clone sure, but not everyone would include this library just to setState
– Shubham Khatri
Nov 23 '17 at 5:36
3
3
Doesn't this violate reactjs.org/docs/… ? If two changes to the nested object got batched the last change would overwrite the first. So the most correct way would be:
this.setState((prevState) => ({nested: {...prevState.nested, propertyToSet: newValue}})
A bit tedious though...– olejorgenb
Dec 21 '17 at 20:55
Doesn't this violate reactjs.org/docs/… ? If two changes to the nested object got batched the last change would overwrite the first. So the most correct way would be:
this.setState((prevState) => ({nested: {...prevState.nested, propertyToSet: newValue}})
A bit tedious though...– olejorgenb
Dec 21 '17 at 20:55
1
1
I don't think you need the
...prevState,
in the last example, you only someProperty
and its children– Jemar Jones
May 25 at 19:43
I don't think you need the
...prevState,
in the last example, you only someProperty
and its children– Jemar Jones
May 25 at 19:43
|
show 7 more comments
up vote
57
down vote
To write it in one line
this.setState({ someProperty: { ...this.state.someProperty, flag: false} });
3
It is recommended to use the updater for setState instead of directly accessing this.state within setState. reactjs.org/docs/react-component.html#setstate
– Raghu Teja
May 23 at 12:52
I believe that's saying, don't readthis.state
immediately aftersetState
and expect it to have the new value. Versus callingthis.state
withinsetState
. Or is there also an issue with the latter that isn't clear to me?
– trpt4him
Jun 1 at 13:06
As trpt4him said, the link you gave talks about the problem of accessingthis.state
aftersetState
. Besides, we are not passingthis.state
tosetState
. The…
operator spread properties. It is the same as Object.assign(). github.com/tc39/proposal-object-rest-spread
– Yoseph
Jun 4 at 4:27
add a comment |
up vote
57
down vote
To write it in one line
this.setState({ someProperty: { ...this.state.someProperty, flag: false} });
3
It is recommended to use the updater for setState instead of directly accessing this.state within setState. reactjs.org/docs/react-component.html#setstate
– Raghu Teja
May 23 at 12:52
I believe that's saying, don't readthis.state
immediately aftersetState
and expect it to have the new value. Versus callingthis.state
withinsetState
. Or is there also an issue with the latter that isn't clear to me?
– trpt4him
Jun 1 at 13:06
As trpt4him said, the link you gave talks about the problem of accessingthis.state
aftersetState
. Besides, we are not passingthis.state
tosetState
. The…
operator spread properties. It is the same as Object.assign(). github.com/tc39/proposal-object-rest-spread
– Yoseph
Jun 4 at 4:27
add a comment |
up vote
57
down vote
up vote
57
down vote
To write it in one line
this.setState({ someProperty: { ...this.state.someProperty, flag: false} });
To write it in one line
this.setState({ someProperty: { ...this.state.someProperty, flag: false} });
answered Aug 9 '17 at 22:07
Yoseph
948813
948813
3
It is recommended to use the updater for setState instead of directly accessing this.state within setState. reactjs.org/docs/react-component.html#setstate
– Raghu Teja
May 23 at 12:52
I believe that's saying, don't readthis.state
immediately aftersetState
and expect it to have the new value. Versus callingthis.state
withinsetState
. Or is there also an issue with the latter that isn't clear to me?
– trpt4him
Jun 1 at 13:06
As trpt4him said, the link you gave talks about the problem of accessingthis.state
aftersetState
. Besides, we are not passingthis.state
tosetState
. The…
operator spread properties. It is the same as Object.assign(). github.com/tc39/proposal-object-rest-spread
– Yoseph
Jun 4 at 4:27
add a comment |
3
It is recommended to use the updater for setState instead of directly accessing this.state within setState. reactjs.org/docs/react-component.html#setstate
– Raghu Teja
May 23 at 12:52
I believe that's saying, don't readthis.state
immediately aftersetState
and expect it to have the new value. Versus callingthis.state
withinsetState
. Or is there also an issue with the latter that isn't clear to me?
– trpt4him
Jun 1 at 13:06
As trpt4him said, the link you gave talks about the problem of accessingthis.state
aftersetState
. Besides, we are not passingthis.state
tosetState
. The…
operator spread properties. It is the same as Object.assign(). github.com/tc39/proposal-object-rest-spread
– Yoseph
Jun 4 at 4:27
3
3
It is recommended to use the updater for setState instead of directly accessing this.state within setState. reactjs.org/docs/react-component.html#setstate
– Raghu Teja
May 23 at 12:52
It is recommended to use the updater for setState instead of directly accessing this.state within setState. reactjs.org/docs/react-component.html#setstate
– Raghu Teja
May 23 at 12:52
I believe that's saying, don't read
this.state
immediately after setState
and expect it to have the new value. Versus calling this.state
within setState
. Or is there also an issue with the latter that isn't clear to me?– trpt4him
Jun 1 at 13:06
I believe that's saying, don't read
this.state
immediately after setState
and expect it to have the new value. Versus calling this.state
within setState
. Or is there also an issue with the latter that isn't clear to me?– trpt4him
Jun 1 at 13:06
As trpt4him said, the link you gave talks about the problem of accessing
this.state
after setState
. Besides, we are not passing this.state
to setState
. The …
operator spread properties. It is the same as Object.assign(). github.com/tc39/proposal-object-rest-spread– Yoseph
Jun 4 at 4:27
As trpt4him said, the link you gave talks about the problem of accessing
this.state
after setState
. Besides, we are not passing this.state
to setState
. The …
operator spread properties. It is the same as Object.assign(). github.com/tc39/proposal-object-rest-spread– Yoseph
Jun 4 at 4:27
add a comment |
up vote
23
down vote
Sometimes direct answers are not the best ones :)
Short version:
this code
this.state = {
someProperty: {
flag: true
}
}
should be simplified as something like
this.state = {
somePropertyFlag: true
}
Long version:
Currently you shouldn't want to work with nested state in React. Because React is not oriented to work with nested states and all solutions proposed here look as hacks. They don't use the framework but fight with it. They suggest to write not so clear code for doubtful purpose of grouping some properties. So they are very interesting as an answer to the challenge but practically useless.
Lets imagine the following state:
{
parent: {
child1: 'value 1',
child2: 'value 2',
...
child100: 'value 100'
}
}
What will happen if you change just a value of child1
? React will not re-render the view because it uses shallow comparison and it will find that parent
property didn't change. BTW mutating the state object directly is considered to be a bad practice in general.
So you need to re-create the whole parent
object. But in this case we will meet another problem. React will think that all children have changed their values and will re-render all of them. Of course it is not good for performance.
It is still possible to solve that problem by writing some complicated logic in shouldComponentUpdate()
but I would prefer to stop here and use simple solution from the short version.
I think there are use cases where a nested state is perfectly fine. E.g. a state dynamically keeping track of key-value pairs. See here how you can update the state for just a single entry in the state: reactjs.org/docs/…
– pors
Aug 30 at 13:45
Shallow comparison is used for pure components by default.
– Basel Shishani
Sep 26 at 12:10
add a comment |
up vote
23
down vote
Sometimes direct answers are not the best ones :)
Short version:
this code
this.state = {
someProperty: {
flag: true
}
}
should be simplified as something like
this.state = {
somePropertyFlag: true
}
Long version:
Currently you shouldn't want to work with nested state in React. Because React is not oriented to work with nested states and all solutions proposed here look as hacks. They don't use the framework but fight with it. They suggest to write not so clear code for doubtful purpose of grouping some properties. So they are very interesting as an answer to the challenge but practically useless.
Lets imagine the following state:
{
parent: {
child1: 'value 1',
child2: 'value 2',
...
child100: 'value 100'
}
}
What will happen if you change just a value of child1
? React will not re-render the view because it uses shallow comparison and it will find that parent
property didn't change. BTW mutating the state object directly is considered to be a bad practice in general.
So you need to re-create the whole parent
object. But in this case we will meet another problem. React will think that all children have changed their values and will re-render all of them. Of course it is not good for performance.
It is still possible to solve that problem by writing some complicated logic in shouldComponentUpdate()
but I would prefer to stop here and use simple solution from the short version.
I think there are use cases where a nested state is perfectly fine. E.g. a state dynamically keeping track of key-value pairs. See here how you can update the state for just a single entry in the state: reactjs.org/docs/…
– pors
Aug 30 at 13:45
Shallow comparison is used for pure components by default.
– Basel Shishani
Sep 26 at 12:10
add a comment |
up vote
23
down vote
up vote
23
down vote
Sometimes direct answers are not the best ones :)
Short version:
this code
this.state = {
someProperty: {
flag: true
}
}
should be simplified as something like
this.state = {
somePropertyFlag: true
}
Long version:
Currently you shouldn't want to work with nested state in React. Because React is not oriented to work with nested states and all solutions proposed here look as hacks. They don't use the framework but fight with it. They suggest to write not so clear code for doubtful purpose of grouping some properties. So they are very interesting as an answer to the challenge but practically useless.
Lets imagine the following state:
{
parent: {
child1: 'value 1',
child2: 'value 2',
...
child100: 'value 100'
}
}
What will happen if you change just a value of child1
? React will not re-render the view because it uses shallow comparison and it will find that parent
property didn't change. BTW mutating the state object directly is considered to be a bad practice in general.
So you need to re-create the whole parent
object. But in this case we will meet another problem. React will think that all children have changed their values and will re-render all of them. Of course it is not good for performance.
It is still possible to solve that problem by writing some complicated logic in shouldComponentUpdate()
but I would prefer to stop here and use simple solution from the short version.
Sometimes direct answers are not the best ones :)
Short version:
this code
this.state = {
someProperty: {
flag: true
}
}
should be simplified as something like
this.state = {
somePropertyFlag: true
}
Long version:
Currently you shouldn't want to work with nested state in React. Because React is not oriented to work with nested states and all solutions proposed here look as hacks. They don't use the framework but fight with it. They suggest to write not so clear code for doubtful purpose of grouping some properties. So they are very interesting as an answer to the challenge but practically useless.
Lets imagine the following state:
{
parent: {
child1: 'value 1',
child2: 'value 2',
...
child100: 'value 100'
}
}
What will happen if you change just a value of child1
? React will not re-render the view because it uses shallow comparison and it will find that parent
property didn't change. BTW mutating the state object directly is considered to be a bad practice in general.
So you need to re-create the whole parent
object. But in this case we will meet another problem. React will think that all children have changed their values and will re-render all of them. Of course it is not good for performance.
It is still possible to solve that problem by writing some complicated logic in shouldComponentUpdate()
but I would prefer to stop here and use simple solution from the short version.
answered Jul 2 at 12:18
Konstantin Smolyanin
11.1k53932
11.1k53932
I think there are use cases where a nested state is perfectly fine. E.g. a state dynamically keeping track of key-value pairs. See here how you can update the state for just a single entry in the state: reactjs.org/docs/…
– pors
Aug 30 at 13:45
Shallow comparison is used for pure components by default.
– Basel Shishani
Sep 26 at 12:10
add a comment |
I think there are use cases where a nested state is perfectly fine. E.g. a state dynamically keeping track of key-value pairs. See here how you can update the state for just a single entry in the state: reactjs.org/docs/…
– pors
Aug 30 at 13:45
Shallow comparison is used for pure components by default.
– Basel Shishani
Sep 26 at 12:10
I think there are use cases where a nested state is perfectly fine. E.g. a state dynamically keeping track of key-value pairs. See here how you can update the state for just a single entry in the state: reactjs.org/docs/…
– pors
Aug 30 at 13:45
I think there are use cases where a nested state is perfectly fine. E.g. a state dynamically keeping track of key-value pairs. See here how you can update the state for just a single entry in the state: reactjs.org/docs/…
– pors
Aug 30 at 13:45
Shallow comparison is used for pure components by default.
– Basel Shishani
Sep 26 at 12:10
Shallow comparison is used for pure components by default.
– Basel Shishani
Sep 26 at 12:10
add a comment |
up vote
18
down vote
If you are using ES2015 you have access to the Object.assign. You can use it as follows to update a nested object.
this.setState({
someProperty: Object.assign({}, this.state.someProperty, {flag: false})
});
You merge the updated properties with the existing and use the returned object to update the state.
Edit: Added an empty object as target to the assign function to make sure the state isn't mutated directly as carkod pointed out.
8
And I could be wrong but I don't think you're using React without ES2015.
– Madbreaks
Nov 10 '17 at 18:29
add a comment |
up vote
18
down vote
If you are using ES2015 you have access to the Object.assign. You can use it as follows to update a nested object.
this.setState({
someProperty: Object.assign({}, this.state.someProperty, {flag: false})
});
You merge the updated properties with the existing and use the returned object to update the state.
Edit: Added an empty object as target to the assign function to make sure the state isn't mutated directly as carkod pointed out.
8
And I could be wrong but I don't think you're using React without ES2015.
– Madbreaks
Nov 10 '17 at 18:29
add a comment |
up vote
18
down vote
up vote
18
down vote
If you are using ES2015 you have access to the Object.assign. You can use it as follows to update a nested object.
this.setState({
someProperty: Object.assign({}, this.state.someProperty, {flag: false})
});
You merge the updated properties with the existing and use the returned object to update the state.
Edit: Added an empty object as target to the assign function to make sure the state isn't mutated directly as carkod pointed out.
If you are using ES2015 you have access to the Object.assign. You can use it as follows to update a nested object.
this.setState({
someProperty: Object.assign({}, this.state.someProperty, {flag: false})
});
You merge the updated properties with the existing and use the returned object to update the state.
Edit: Added an empty object as target to the assign function to make sure the state isn't mutated directly as carkod pointed out.
edited Feb 7 at 6:58
answered Oct 7 '17 at 19:20
Alyssa Roose
18915
18915
8
And I could be wrong but I don't think you're using React without ES2015.
– Madbreaks
Nov 10 '17 at 18:29
add a comment |
8
And I could be wrong but I don't think you're using React without ES2015.
– Madbreaks
Nov 10 '17 at 18:29
8
8
And I could be wrong but I don't think you're using React without ES2015.
– Madbreaks
Nov 10 '17 at 18:29
And I could be wrong but I don't think you're using React without ES2015.
– Madbreaks
Nov 10 '17 at 18:29
add a comment |
up vote
12
down vote
Disclaimer
Nested State in React is wrong design
Read this excellent answer.
Reasoning behind this answer:
React's setState is just a built-in convenience, but you soon realise
that it has its limits. Using custom properties and intelligent use of
forceUpdate gives you much more.
eg:
class MyClass extends React.Component {
myState = someObject
inputValue = 42
...
MobX, for example, ditches state completely and uses custom observable properties.
Use Observables instead of state in React components.
the best answer of all - see example here
There is another shorter way to update whatever nested property.
this.setState(state => {
state.nested.flag = false
state.another.deep.prop = true
return state
})
On one line
this.setState(state => (state.nested.flag = false, state))
Of course this is abusing some core principles, as the state
should be read-only, but since you are immediately discarding the old state and replacing it with new state, it is completely ok.
Warning
Even though the component containing the state will update and rerender properly (except this gotcha), the props will fail to propagate to children (see Spymaster's comment below). Only use this technique if you know what you are doing.
For example, you may pass a changed flat prop that is updated and passed easily.
render(
//some complex render with your nested state
<ChildComponent complexNestedProp={this.state.nested} pleaseRerender={Math.random()}/>
)
Now even though reference for complexNestedProp did not change (shouldComponentUpdate)
this.props.complexNestedProp === nextProps.complexNestedProp
the component will rerender whenever parent component updates, which is the case after calling this.setState
or this.forceUpdate
in the parent.
6
You shouldn't be mutating any react state anywhere. If there are nested components that use data from state.nested or state.another, they won't re-render nor will their children. This is because the object did not change, thus React thinks nothing has changed.
– SpyMaster356
May 12 at 16:51
@SpyMaster356 I have updated my answer to address this. Also note that MobX, for example, ditchesstate
completely and uses custom observable properties. React'ssetState
is just a built-in convenience, but you soon realise that it has its limits. Using custom properties and intelligent use offorceUpdate
gives you much more. Use Observables instead of state in React components.
– Qwerty
Aug 9 at 9:44
Also I added an example react-experiments.herokuapp.com/state-flow (link to git at the top)
– Qwerty
Aug 9 at 16:28
add a comment |
up vote
12
down vote
Disclaimer
Nested State in React is wrong design
Read this excellent answer.
Reasoning behind this answer:
React's setState is just a built-in convenience, but you soon realise
that it has its limits. Using custom properties and intelligent use of
forceUpdate gives you much more.
eg:
class MyClass extends React.Component {
myState = someObject
inputValue = 42
...
MobX, for example, ditches state completely and uses custom observable properties.
Use Observables instead of state in React components.
the best answer of all - see example here
There is another shorter way to update whatever nested property.
this.setState(state => {
state.nested.flag = false
state.another.deep.prop = true
return state
})
On one line
this.setState(state => (state.nested.flag = false, state))
Of course this is abusing some core principles, as the state
should be read-only, but since you are immediately discarding the old state and replacing it with new state, it is completely ok.
Warning
Even though the component containing the state will update and rerender properly (except this gotcha), the props will fail to propagate to children (see Spymaster's comment below). Only use this technique if you know what you are doing.
For example, you may pass a changed flat prop that is updated and passed easily.
render(
//some complex render with your nested state
<ChildComponent complexNestedProp={this.state.nested} pleaseRerender={Math.random()}/>
)
Now even though reference for complexNestedProp did not change (shouldComponentUpdate)
this.props.complexNestedProp === nextProps.complexNestedProp
the component will rerender whenever parent component updates, which is the case after calling this.setState
or this.forceUpdate
in the parent.
6
You shouldn't be mutating any react state anywhere. If there are nested components that use data from state.nested or state.another, they won't re-render nor will their children. This is because the object did not change, thus React thinks nothing has changed.
– SpyMaster356
May 12 at 16:51
@SpyMaster356 I have updated my answer to address this. Also note that MobX, for example, ditchesstate
completely and uses custom observable properties. React'ssetState
is just a built-in convenience, but you soon realise that it has its limits. Using custom properties and intelligent use offorceUpdate
gives you much more. Use Observables instead of state in React components.
– Qwerty
Aug 9 at 9:44
Also I added an example react-experiments.herokuapp.com/state-flow (link to git at the top)
– Qwerty
Aug 9 at 16:28
add a comment |
up vote
12
down vote
up vote
12
down vote
Disclaimer
Nested State in React is wrong design
Read this excellent answer.
Reasoning behind this answer:
React's setState is just a built-in convenience, but you soon realise
that it has its limits. Using custom properties and intelligent use of
forceUpdate gives you much more.
eg:
class MyClass extends React.Component {
myState = someObject
inputValue = 42
...
MobX, for example, ditches state completely and uses custom observable properties.
Use Observables instead of state in React components.
the best answer of all - see example here
There is another shorter way to update whatever nested property.
this.setState(state => {
state.nested.flag = false
state.another.deep.prop = true
return state
})
On one line
this.setState(state => (state.nested.flag = false, state))
Of course this is abusing some core principles, as the state
should be read-only, but since you are immediately discarding the old state and replacing it with new state, it is completely ok.
Warning
Even though the component containing the state will update and rerender properly (except this gotcha), the props will fail to propagate to children (see Spymaster's comment below). Only use this technique if you know what you are doing.
For example, you may pass a changed flat prop that is updated and passed easily.
render(
//some complex render with your nested state
<ChildComponent complexNestedProp={this.state.nested} pleaseRerender={Math.random()}/>
)
Now even though reference for complexNestedProp did not change (shouldComponentUpdate)
this.props.complexNestedProp === nextProps.complexNestedProp
the component will rerender whenever parent component updates, which is the case after calling this.setState
or this.forceUpdate
in the parent.
Disclaimer
Nested State in React is wrong design
Read this excellent answer.
Reasoning behind this answer:
React's setState is just a built-in convenience, but you soon realise
that it has its limits. Using custom properties and intelligent use of
forceUpdate gives you much more.
eg:
class MyClass extends React.Component {
myState = someObject
inputValue = 42
...
MobX, for example, ditches state completely and uses custom observable properties.
Use Observables instead of state in React components.
the best answer of all - see example here
There is another shorter way to update whatever nested property.
this.setState(state => {
state.nested.flag = false
state.another.deep.prop = true
return state
})
On one line
this.setState(state => (state.nested.flag = false, state))
Of course this is abusing some core principles, as the state
should be read-only, but since you are immediately discarding the old state and replacing it with new state, it is completely ok.
Warning
Even though the component containing the state will update and rerender properly (except this gotcha), the props will fail to propagate to children (see Spymaster's comment below). Only use this technique if you know what you are doing.
For example, you may pass a changed flat prop that is updated and passed easily.
render(
//some complex render with your nested state
<ChildComponent complexNestedProp={this.state.nested} pleaseRerender={Math.random()}/>
)
Now even though reference for complexNestedProp did not change (shouldComponentUpdate)
this.props.complexNestedProp === nextProps.complexNestedProp
the component will rerender whenever parent component updates, which is the case after calling this.setState
or this.forceUpdate
in the parent.
edited Oct 18 at 10:04
answered May 3 at 7:50
Qwerty
8,67375067
8,67375067
6
You shouldn't be mutating any react state anywhere. If there are nested components that use data from state.nested or state.another, they won't re-render nor will their children. This is because the object did not change, thus React thinks nothing has changed.
– SpyMaster356
May 12 at 16:51
@SpyMaster356 I have updated my answer to address this. Also note that MobX, for example, ditchesstate
completely and uses custom observable properties. React'ssetState
is just a built-in convenience, but you soon realise that it has its limits. Using custom properties and intelligent use offorceUpdate
gives you much more. Use Observables instead of state in React components.
– Qwerty
Aug 9 at 9:44
Also I added an example react-experiments.herokuapp.com/state-flow (link to git at the top)
– Qwerty
Aug 9 at 16:28
add a comment |
6
You shouldn't be mutating any react state anywhere. If there are nested components that use data from state.nested or state.another, they won't re-render nor will their children. This is because the object did not change, thus React thinks nothing has changed.
– SpyMaster356
May 12 at 16:51
@SpyMaster356 I have updated my answer to address this. Also note that MobX, for example, ditchesstate
completely and uses custom observable properties. React'ssetState
is just a built-in convenience, but you soon realise that it has its limits. Using custom properties and intelligent use offorceUpdate
gives you much more. Use Observables instead of state in React components.
– Qwerty
Aug 9 at 9:44
Also I added an example react-experiments.herokuapp.com/state-flow (link to git at the top)
– Qwerty
Aug 9 at 16:28
6
6
You shouldn't be mutating any react state anywhere. If there are nested components that use data from state.nested or state.another, they won't re-render nor will their children. This is because the object did not change, thus React thinks nothing has changed.
– SpyMaster356
May 12 at 16:51
You shouldn't be mutating any react state anywhere. If there are nested components that use data from state.nested or state.another, they won't re-render nor will their children. This is because the object did not change, thus React thinks nothing has changed.
– SpyMaster356
May 12 at 16:51
@SpyMaster356 I have updated my answer to address this. Also note that MobX, for example, ditches
state
completely and uses custom observable properties. React's setState
is just a built-in convenience, but you soon realise that it has its limits. Using custom properties and intelligent use of forceUpdate
gives you much more. Use Observables instead of state in React components.– Qwerty
Aug 9 at 9:44
@SpyMaster356 I have updated my answer to address this. Also note that MobX, for example, ditches
state
completely and uses custom observable properties. React's setState
is just a built-in convenience, but you soon realise that it has its limits. Using custom properties and intelligent use of forceUpdate
gives you much more. Use Observables instead of state in React components.– Qwerty
Aug 9 at 9:44
Also I added an example react-experiments.herokuapp.com/state-flow (link to git at the top)
– Qwerty
Aug 9 at 16:28
Also I added an example react-experiments.herokuapp.com/state-flow (link to git at the top)
– Qwerty
Aug 9 at 16:28
add a comment |
up vote
6
down vote
There are many libraries to help with this. For example, using immutability-helper:
import update from 'immutability-helper';
const newState = update(this.state, {
someProperty: {flag: {$set: false}},
};
this.setState(newState);
Or using lodash/fp:
import {merge} from 'lodash/fp';
const newState = merge(this.state, {
someProperty: {flag: false},
});
If you uselodash
, you probably want to try_.cloneDeep
and set state to be the cloned copy.
– Yixing Liu
Jul 26 at 20:37
@YixingLiu: I've updated the answer to uselodash/fp
, so we don't have to worry about mutations.
– tokland
Jul 26 at 20:50
add a comment |
up vote
6
down vote
There are many libraries to help with this. For example, using immutability-helper:
import update from 'immutability-helper';
const newState = update(this.state, {
someProperty: {flag: {$set: false}},
};
this.setState(newState);
Or using lodash/fp:
import {merge} from 'lodash/fp';
const newState = merge(this.state, {
someProperty: {flag: false},
});
If you uselodash
, you probably want to try_.cloneDeep
and set state to be the cloned copy.
– Yixing Liu
Jul 26 at 20:37
@YixingLiu: I've updated the answer to uselodash/fp
, so we don't have to worry about mutations.
– tokland
Jul 26 at 20:50
add a comment |
up vote
6
down vote
up vote
6
down vote
There are many libraries to help with this. For example, using immutability-helper:
import update from 'immutability-helper';
const newState = update(this.state, {
someProperty: {flag: {$set: false}},
};
this.setState(newState);
Or using lodash/fp:
import {merge} from 'lodash/fp';
const newState = merge(this.state, {
someProperty: {flag: false},
});
There are many libraries to help with this. For example, using immutability-helper:
import update from 'immutability-helper';
const newState = update(this.state, {
someProperty: {flag: {$set: false}},
};
this.setState(newState);
Or using lodash/fp:
import {merge} from 'lodash/fp';
const newState = merge(this.state, {
someProperty: {flag: false},
});
edited Aug 13 at 18:42
answered May 19 '17 at 8:15
tokland
51.2k11114142
51.2k11114142
If you uselodash
, you probably want to try_.cloneDeep
and set state to be the cloned copy.
– Yixing Liu
Jul 26 at 20:37
@YixingLiu: I've updated the answer to uselodash/fp
, so we don't have to worry about mutations.
– tokland
Jul 26 at 20:50
add a comment |
If you uselodash
, you probably want to try_.cloneDeep
and set state to be the cloned copy.
– Yixing Liu
Jul 26 at 20:37
@YixingLiu: I've updated the answer to uselodash/fp
, so we don't have to worry about mutations.
– tokland
Jul 26 at 20:50
If you use
lodash
, you probably want to try _.cloneDeep
and set state to be the cloned copy.– Yixing Liu
Jul 26 at 20:37
If you use
lodash
, you probably want to try _.cloneDeep
and set state to be the cloned copy.– Yixing Liu
Jul 26 at 20:37
@YixingLiu: I've updated the answer to use
lodash/fp
, so we don't have to worry about mutations.– tokland
Jul 26 at 20:50
@YixingLiu: I've updated the answer to use
lodash/fp
, so we don't have to worry about mutations.– tokland
Jul 26 at 20:50
add a comment |
up vote
5
down vote
Here's a variation on the first answer given in this thread which doesn't require any extra packages, libraries or special functions.
state = {
someProperty: {
flag: 'string'
}
}
handleChange = (value) => {
const newState = {...this.state.someProperty, flag: value}
this.setState({ someProperty: newState })
}
In order to set the state of a specific nested field, you have set the whole object. I did this by creating a variable, newState
and spreading the contents of the current state into it first using the ES2015 spread operator. Then, I replaced the value of this.state.flag
with the new value (since I set flag: value
after I spread the current state into the object, the flag
field in the current state is overridden). Then, I simply set the state of someProperty
to my newState
object.
add a comment |
up vote
5
down vote
Here's a variation on the first answer given in this thread which doesn't require any extra packages, libraries or special functions.
state = {
someProperty: {
flag: 'string'
}
}
handleChange = (value) => {
const newState = {...this.state.someProperty, flag: value}
this.setState({ someProperty: newState })
}
In order to set the state of a specific nested field, you have set the whole object. I did this by creating a variable, newState
and spreading the contents of the current state into it first using the ES2015 spread operator. Then, I replaced the value of this.state.flag
with the new value (since I set flag: value
after I spread the current state into the object, the flag
field in the current state is overridden). Then, I simply set the state of someProperty
to my newState
object.
add a comment |
up vote
5
down vote
up vote
5
down vote
Here's a variation on the first answer given in this thread which doesn't require any extra packages, libraries or special functions.
state = {
someProperty: {
flag: 'string'
}
}
handleChange = (value) => {
const newState = {...this.state.someProperty, flag: value}
this.setState({ someProperty: newState })
}
In order to set the state of a specific nested field, you have set the whole object. I did this by creating a variable, newState
and spreading the contents of the current state into it first using the ES2015 spread operator. Then, I replaced the value of this.state.flag
with the new value (since I set flag: value
after I spread the current state into the object, the flag
field in the current state is overridden). Then, I simply set the state of someProperty
to my newState
object.
Here's a variation on the first answer given in this thread which doesn't require any extra packages, libraries or special functions.
state = {
someProperty: {
flag: 'string'
}
}
handleChange = (value) => {
const newState = {...this.state.someProperty, flag: value}
this.setState({ someProperty: newState })
}
In order to set the state of a specific nested field, you have set the whole object. I did this by creating a variable, newState
and spreading the contents of the current state into it first using the ES2015 spread operator. Then, I replaced the value of this.state.flag
with the new value (since I set flag: value
after I spread the current state into the object, the flag
field in the current state is overridden). Then, I simply set the state of someProperty
to my newState
object.
answered Nov 29 '17 at 17:10
Matthew Berkompas
6112
6112
add a comment |
add a comment |
up vote
5
down vote
You can also go this way (which feels more readable to me):
const newState = Object.assign({}, this.state);
newState.property.nestedProperty = 'newValue';
this.setState(newState);
add a comment |
up vote
5
down vote
You can also go this way (which feels more readable to me):
const newState = Object.assign({}, this.state);
newState.property.nestedProperty = 'newValue';
this.setState(newState);
add a comment |
up vote
5
down vote
up vote
5
down vote
You can also go this way (which feels more readable to me):
const newState = Object.assign({}, this.state);
newState.property.nestedProperty = 'newValue';
this.setState(newState);
You can also go this way (which feels more readable to me):
const newState = Object.assign({}, this.state);
newState.property.nestedProperty = 'newValue';
this.setState(newState);
answered Apr 7 at 13:30
enzolito
7817
7817
add a comment |
add a comment |
up vote
2
down vote
I used this solution.
If you have a nested state like this:
this.state = {
formInputs:{
friendName:{
value:'',
isValid:false,
errorMsg:''
},
friendEmail:{
value:'',
isValid:false,
errorMsg:''
}
}
you can declare the handleChange function that copy current status and re-assigns it with changed values
handleChange(el) {
let inputName = el.target.name;
let inputValue = el.target.value;
let statusCopy = Object.assign({}, this.state);
statusCopy.formInputs[inputName].value = inputValue;
this.setState(statusCopy);
}
here the html with the event listener
<input type="text" onChange={this.handleChange} " name="friendName" />
add a comment |
up vote
2
down vote
I used this solution.
If you have a nested state like this:
this.state = {
formInputs:{
friendName:{
value:'',
isValid:false,
errorMsg:''
},
friendEmail:{
value:'',
isValid:false,
errorMsg:''
}
}
you can declare the handleChange function that copy current status and re-assigns it with changed values
handleChange(el) {
let inputName = el.target.name;
let inputValue = el.target.value;
let statusCopy = Object.assign({}, this.state);
statusCopy.formInputs[inputName].value = inputValue;
this.setState(statusCopy);
}
here the html with the event listener
<input type="text" onChange={this.handleChange} " name="friendName" />
add a comment |
up vote
2
down vote
up vote
2
down vote
I used this solution.
If you have a nested state like this:
this.state = {
formInputs:{
friendName:{
value:'',
isValid:false,
errorMsg:''
},
friendEmail:{
value:'',
isValid:false,
errorMsg:''
}
}
you can declare the handleChange function that copy current status and re-assigns it with changed values
handleChange(el) {
let inputName = el.target.name;
let inputValue = el.target.value;
let statusCopy = Object.assign({}, this.state);
statusCopy.formInputs[inputName].value = inputValue;
this.setState(statusCopy);
}
here the html with the event listener
<input type="text" onChange={this.handleChange} " name="friendName" />
I used this solution.
If you have a nested state like this:
this.state = {
formInputs:{
friendName:{
value:'',
isValid:false,
errorMsg:''
},
friendEmail:{
value:'',
isValid:false,
errorMsg:''
}
}
you can declare the handleChange function that copy current status and re-assigns it with changed values
handleChange(el) {
let inputName = el.target.name;
let inputValue = el.target.value;
let statusCopy = Object.assign({}, this.state);
statusCopy.formInputs[inputName].value = inputValue;
this.setState(statusCopy);
}
here the html with the event listener
<input type="text" onChange={this.handleChange} " name="friendName" />
answered Mar 6 at 9:40
Alberto Piras
813
813
add a comment |
add a comment |
up vote
2
down vote
Two other options not mentioned yet:
- If you have deeply nested state, consider if you can restructure the child objects to sit at the root. This makes the data easier to update.
- There are many handy libraries available for handling immutable state listed in the Redux docs. I recommend Immer since it allows you to write code in a mutative manner but handles the necessary cloning behind the scenes. It also freezes the resulting object so you can't accidentally mutate it later.
add a comment |
up vote
2
down vote
Two other options not mentioned yet:
- If you have deeply nested state, consider if you can restructure the child objects to sit at the root. This makes the data easier to update.
- There are many handy libraries available for handling immutable state listed in the Redux docs. I recommend Immer since it allows you to write code in a mutative manner but handles the necessary cloning behind the scenes. It also freezes the resulting object so you can't accidentally mutate it later.
add a comment |
up vote
2
down vote
up vote
2
down vote
Two other options not mentioned yet:
- If you have deeply nested state, consider if you can restructure the child objects to sit at the root. This makes the data easier to update.
- There are many handy libraries available for handling immutable state listed in the Redux docs. I recommend Immer since it allows you to write code in a mutative manner but handles the necessary cloning behind the scenes. It also freezes the resulting object so you can't accidentally mutate it later.
Two other options not mentioned yet:
- If you have deeply nested state, consider if you can restructure the child objects to sit at the root. This makes the data easier to update.
- There are many handy libraries available for handling immutable state listed in the Redux docs. I recommend Immer since it allows you to write code in a mutative manner but handles the necessary cloning behind the scenes. It also freezes the resulting object so you can't accidentally mutate it later.
edited Jun 28 at 2:57
answered Jun 28 at 2:51
Cory House
7,82775674
7,82775674
add a comment |
add a comment |
up vote
1
down vote
To make things generic, I worked on @ShubhamKhatri's and @Qwerty's answers.
state object
this.state = {
name: '',
grandParent: {
parent1: {
child: ''
},
parent2: {
child: ''
}
}
};
input controls
<input
value={this.state.name}
onChange={this.updateState}
type="text"
name="name"
/>
<input
value={this.state.grandParent.parent1.child}
onChange={this.updateState}
type="text"
name="grandParent.parent1.child"
/>
<input
value={this.state.grandParent.parent2.child}
onChange={this.updateState}
type="text"
name="grandParent.parent2.child"
/>
updateState method
setState as @ShubhamKhatri's answer
updateState(event) {
const path = event.target.name.split('.');
const depth = path.length;
const oldstate = this.state;
const newstate = { ...oldstate };
let newStateLevel = newstate;
let oldStateLevel = oldstate;
for (let i = 0; i < depth; i += 1) {
if (i === depth - 1) {
newStateLevel[path[i]] = event.target.value;
} else {
newStateLevel[path[i]] = { ...oldStateLevel[path[i]] };
oldStateLevel = oldStateLevel[path[i]];
newStateLevel = newStateLevel[path[i]];
}
}
this.setState(newstate);
}
setState as @Qwerty's answer
updateState(event) {
const path = event.target.name.split('.');
const depth = path.length;
const state = { ...this.state };
let ref = state;
for (let i = 0; i < depth; i += 1) {
if (i === depth - 1) {
ref[path[i]] = event.target.value;
} else {
ref = ref[path[i]];
}
}
this.setState(state);
}
Note: These above methods won't work for arrays
add a comment |
up vote
1
down vote
To make things generic, I worked on @ShubhamKhatri's and @Qwerty's answers.
state object
this.state = {
name: '',
grandParent: {
parent1: {
child: ''
},
parent2: {
child: ''
}
}
};
input controls
<input
value={this.state.name}
onChange={this.updateState}
type="text"
name="name"
/>
<input
value={this.state.grandParent.parent1.child}
onChange={this.updateState}
type="text"
name="grandParent.parent1.child"
/>
<input
value={this.state.grandParent.parent2.child}
onChange={this.updateState}
type="text"
name="grandParent.parent2.child"
/>
updateState method
setState as @ShubhamKhatri's answer
updateState(event) {
const path = event.target.name.split('.');
const depth = path.length;
const oldstate = this.state;
const newstate = { ...oldstate };
let newStateLevel = newstate;
let oldStateLevel = oldstate;
for (let i = 0; i < depth; i += 1) {
if (i === depth - 1) {
newStateLevel[path[i]] = event.target.value;
} else {
newStateLevel[path[i]] = { ...oldStateLevel[path[i]] };
oldStateLevel = oldStateLevel[path[i]];
newStateLevel = newStateLevel[path[i]];
}
}
this.setState(newstate);
}
setState as @Qwerty's answer
updateState(event) {
const path = event.target.name.split('.');
const depth = path.length;
const state = { ...this.state };
let ref = state;
for (let i = 0; i < depth; i += 1) {
if (i === depth - 1) {
ref[path[i]] = event.target.value;
} else {
ref = ref[path[i]];
}
}
this.setState(state);
}
Note: These above methods won't work for arrays
add a comment |
up vote
1
down vote
up vote
1
down vote
To make things generic, I worked on @ShubhamKhatri's and @Qwerty's answers.
state object
this.state = {
name: '',
grandParent: {
parent1: {
child: ''
},
parent2: {
child: ''
}
}
};
input controls
<input
value={this.state.name}
onChange={this.updateState}
type="text"
name="name"
/>
<input
value={this.state.grandParent.parent1.child}
onChange={this.updateState}
type="text"
name="grandParent.parent1.child"
/>
<input
value={this.state.grandParent.parent2.child}
onChange={this.updateState}
type="text"
name="grandParent.parent2.child"
/>
updateState method
setState as @ShubhamKhatri's answer
updateState(event) {
const path = event.target.name.split('.');
const depth = path.length;
const oldstate = this.state;
const newstate = { ...oldstate };
let newStateLevel = newstate;
let oldStateLevel = oldstate;
for (let i = 0; i < depth; i += 1) {
if (i === depth - 1) {
newStateLevel[path[i]] = event.target.value;
} else {
newStateLevel[path[i]] = { ...oldStateLevel[path[i]] };
oldStateLevel = oldStateLevel[path[i]];
newStateLevel = newStateLevel[path[i]];
}
}
this.setState(newstate);
}
setState as @Qwerty's answer
updateState(event) {
const path = event.target.name.split('.');
const depth = path.length;
const state = { ...this.state };
let ref = state;
for (let i = 0; i < depth; i += 1) {
if (i === depth - 1) {
ref[path[i]] = event.target.value;
} else {
ref = ref[path[i]];
}
}
this.setState(state);
}
Note: These above methods won't work for arrays
To make things generic, I worked on @ShubhamKhatri's and @Qwerty's answers.
state object
this.state = {
name: '',
grandParent: {
parent1: {
child: ''
},
parent2: {
child: ''
}
}
};
input controls
<input
value={this.state.name}
onChange={this.updateState}
type="text"
name="name"
/>
<input
value={this.state.grandParent.parent1.child}
onChange={this.updateState}
type="text"
name="grandParent.parent1.child"
/>
<input
value={this.state.grandParent.parent2.child}
onChange={this.updateState}
type="text"
name="grandParent.parent2.child"
/>
updateState method
setState as @ShubhamKhatri's answer
updateState(event) {
const path = event.target.name.split('.');
const depth = path.length;
const oldstate = this.state;
const newstate = { ...oldstate };
let newStateLevel = newstate;
let oldStateLevel = oldstate;
for (let i = 0; i < depth; i += 1) {
if (i === depth - 1) {
newStateLevel[path[i]] = event.target.value;
} else {
newStateLevel[path[i]] = { ...oldStateLevel[path[i]] };
oldStateLevel = oldStateLevel[path[i]];
newStateLevel = newStateLevel[path[i]];
}
}
this.setState(newstate);
}
setState as @Qwerty's answer
updateState(event) {
const path = event.target.name.split('.');
const depth = path.length;
const state = { ...this.state };
let ref = state;
for (let i = 0; i < depth; i += 1) {
if (i === depth - 1) {
ref[path[i]] = event.target.value;
} else {
ref = ref[path[i]];
}
}
this.setState(state);
}
Note: These above methods won't work for arrays
answered Jul 2 at 8:29
Venugopal
1,62911227
1,62911227
add a comment |
add a comment |
up vote
1
down vote
We use Immer https://github.com/mweststrate/immer to handle these kinds of issues.
Just replaced this code in one of our components
this.setState(prevState => ({
...prevState,
preferences: {
...prevState.preferences,
[key]: newValue
}
}));
With this
import produce from 'immer';
this.setState(produce(draft => {
draft.preferences[key] = newValue;
}));
With immer you handle your state as a "normal object".
The magic happens behind the scene with proxy objects.
add a comment |
up vote
1
down vote
We use Immer https://github.com/mweststrate/immer to handle these kinds of issues.
Just replaced this code in one of our components
this.setState(prevState => ({
...prevState,
preferences: {
...prevState.preferences,
[key]: newValue
}
}));
With this
import produce from 'immer';
this.setState(produce(draft => {
draft.preferences[key] = newValue;
}));
With immer you handle your state as a "normal object".
The magic happens behind the scene with proxy objects.
add a comment |
up vote
1
down vote
up vote
1
down vote
We use Immer https://github.com/mweststrate/immer to handle these kinds of issues.
Just replaced this code in one of our components
this.setState(prevState => ({
...prevState,
preferences: {
...prevState.preferences,
[key]: newValue
}
}));
With this
import produce from 'immer';
this.setState(produce(draft => {
draft.preferences[key] = newValue;
}));
With immer you handle your state as a "normal object".
The magic happens behind the scene with proxy objects.
We use Immer https://github.com/mweststrate/immer to handle these kinds of issues.
Just replaced this code in one of our components
this.setState(prevState => ({
...prevState,
preferences: {
...prevState.preferences,
[key]: newValue
}
}));
With this
import produce from 'immer';
this.setState(produce(draft => {
draft.preferences[key] = newValue;
}));
With immer you handle your state as a "normal object".
The magic happens behind the scene with proxy objects.
answered Aug 24 at 9:40
Joakim Jäderberg
111
111
add a comment |
add a comment |
up vote
1
down vote
Create a copy of the state:let someProperty = JSON.parse(JSON.stringify(this.state.someProperty))
make changes in this object:someProperty.flag = "false"
now update the state
this.setState({someProperty})
add a comment |
up vote
1
down vote
Create a copy of the state:let someProperty = JSON.parse(JSON.stringify(this.state.someProperty))
make changes in this object:someProperty.flag = "false"
now update the state
this.setState({someProperty})
add a comment |
up vote
1
down vote
up vote
1
down vote
Create a copy of the state:let someProperty = JSON.parse(JSON.stringify(this.state.someProperty))
make changes in this object:someProperty.flag = "false"
now update the state
this.setState({someProperty})
Create a copy of the state:let someProperty = JSON.parse(JSON.stringify(this.state.someProperty))
make changes in this object:someProperty.flag = "false"
now update the state
this.setState({someProperty})
answered Oct 18 at 9:51
Dhakad
154
154
add a comment |
add a comment |
up vote
0
down vote
I found this to work for me, having a project form in my case where for example you have an id, and a name and I'd rather maintain state for a nested project.
return (
<div>
<h2>Project Details</h2>
<form>
<Input label="ID" group type="number" value={this.state.project.id} onChange={(event) => this.setState({ project: {...this.state.project, id: event.target.value}})} />
<Input label="Name" group type="text" value={this.state.project.name} onChange={(event) => this.setState({ project: {...this.state.project, name: event.target.value}})} />
</form>
</div>
)
Let me know!
add a comment |
up vote
0
down vote
I found this to work for me, having a project form in my case where for example you have an id, and a name and I'd rather maintain state for a nested project.
return (
<div>
<h2>Project Details</h2>
<form>
<Input label="ID" group type="number" value={this.state.project.id} onChange={(event) => this.setState({ project: {...this.state.project, id: event.target.value}})} />
<Input label="Name" group type="text" value={this.state.project.name} onChange={(event) => this.setState({ project: {...this.state.project, name: event.target.value}})} />
</form>
</div>
)
Let me know!
add a comment |
up vote
0
down vote
up vote
0
down vote
I found this to work for me, having a project form in my case where for example you have an id, and a name and I'd rather maintain state for a nested project.
return (
<div>
<h2>Project Details</h2>
<form>
<Input label="ID" group type="number" value={this.state.project.id} onChange={(event) => this.setState({ project: {...this.state.project, id: event.target.value}})} />
<Input label="Name" group type="text" value={this.state.project.name} onChange={(event) => this.setState({ project: {...this.state.project, name: event.target.value}})} />
</form>
</div>
)
Let me know!
I found this to work for me, having a project form in my case where for example you have an id, and a name and I'd rather maintain state for a nested project.
return (
<div>
<h2>Project Details</h2>
<form>
<Input label="ID" group type="number" value={this.state.project.id} onChange={(event) => this.setState({ project: {...this.state.project, id: event.target.value}})} />
<Input label="Name" group type="text" value={this.state.project.name} onChange={(event) => this.setState({ project: {...this.state.project, name: event.target.value}})} />
</form>
</div>
)
Let me know!
answered Jun 13 at 15:36
Michael Stokes
133214
133214
add a comment |
add a comment |
up vote
0
down vote
Something like this might suffice,
const isObject = (thing) => {
if(thing &&
typeof thing === 'object' &&
typeof thing !== null
&& !(Array.isArray(thing))
){
return true;
}
return false;
}
/*
Call with an array containing the path to the property you want to access
And the current component/redux state.
For example if we want to update `hello` within the following obj
const obj = {
somePrimitive:false,
someNestedObj:{
hello:1
}
}
we would do :
//clone the object
const cloned = clone(['someNestedObj','hello'],obj)
//Set the new value
cloned.someNestedObj.hello = 5;
*/
const clone = (arr, state) => {
let clonedObj = {...state}
const originalObj = clonedObj;
arr.forEach(property => {
if(!(property in clonedObj)){
throw new Error('State missing property')
}
if(isObject(clonedObj[property])){
clonedObj[property] = {...originalObj[property]};
clonedObj = clonedObj[property];
}
})
return originalObj;
}
const nestedObj = {
someProperty:true,
someNestedObj:{
someOtherProperty:true
}
}
const clonedObj = clone(['someProperty'], nestedObj);
console.log(clonedObj === nestedObj) //returns false
console.log(clonedObj.someProperty === nestedObj.someProperty) //returns true
console.log(clonedObj.someNestedObj === nestedObj.someNestedObj) //returns true
console.log()
const clonedObj2 = clone(['someProperty','someNestedObj','someOtherProperty'], nestedObj);
console.log(clonedObj2 === nestedObj) // returns false
console.log(clonedObj2.someNestedObj === nestedObj.someNestedObj) //returns false
//returns true (doesn't attempt to clone because its primitive type)
console.log(clonedObj2.someNestedObj.someOtherProperty === nestedObj.someNestedObj.someOtherProperty)
add a comment |
up vote
0
down vote
Something like this might suffice,
const isObject = (thing) => {
if(thing &&
typeof thing === 'object' &&
typeof thing !== null
&& !(Array.isArray(thing))
){
return true;
}
return false;
}
/*
Call with an array containing the path to the property you want to access
And the current component/redux state.
For example if we want to update `hello` within the following obj
const obj = {
somePrimitive:false,
someNestedObj:{
hello:1
}
}
we would do :
//clone the object
const cloned = clone(['someNestedObj','hello'],obj)
//Set the new value
cloned.someNestedObj.hello = 5;
*/
const clone = (arr, state) => {
let clonedObj = {...state}
const originalObj = clonedObj;
arr.forEach(property => {
if(!(property in clonedObj)){
throw new Error('State missing property')
}
if(isObject(clonedObj[property])){
clonedObj[property] = {...originalObj[property]};
clonedObj = clonedObj[property];
}
})
return originalObj;
}
const nestedObj = {
someProperty:true,
someNestedObj:{
someOtherProperty:true
}
}
const clonedObj = clone(['someProperty'], nestedObj);
console.log(clonedObj === nestedObj) //returns false
console.log(clonedObj.someProperty === nestedObj.someProperty) //returns true
console.log(clonedObj.someNestedObj === nestedObj.someNestedObj) //returns true
console.log()
const clonedObj2 = clone(['someProperty','someNestedObj','someOtherProperty'], nestedObj);
console.log(clonedObj2 === nestedObj) // returns false
console.log(clonedObj2.someNestedObj === nestedObj.someNestedObj) //returns false
//returns true (doesn't attempt to clone because its primitive type)
console.log(clonedObj2.someNestedObj.someOtherProperty === nestedObj.someNestedObj.someOtherProperty)
add a comment |
up vote
0
down vote
up vote
0
down vote
Something like this might suffice,
const isObject = (thing) => {
if(thing &&
typeof thing === 'object' &&
typeof thing !== null
&& !(Array.isArray(thing))
){
return true;
}
return false;
}
/*
Call with an array containing the path to the property you want to access
And the current component/redux state.
For example if we want to update `hello` within the following obj
const obj = {
somePrimitive:false,
someNestedObj:{
hello:1
}
}
we would do :
//clone the object
const cloned = clone(['someNestedObj','hello'],obj)
//Set the new value
cloned.someNestedObj.hello = 5;
*/
const clone = (arr, state) => {
let clonedObj = {...state}
const originalObj = clonedObj;
arr.forEach(property => {
if(!(property in clonedObj)){
throw new Error('State missing property')
}
if(isObject(clonedObj[property])){
clonedObj[property] = {...originalObj[property]};
clonedObj = clonedObj[property];
}
})
return originalObj;
}
const nestedObj = {
someProperty:true,
someNestedObj:{
someOtherProperty:true
}
}
const clonedObj = clone(['someProperty'], nestedObj);
console.log(clonedObj === nestedObj) //returns false
console.log(clonedObj.someProperty === nestedObj.someProperty) //returns true
console.log(clonedObj.someNestedObj === nestedObj.someNestedObj) //returns true
console.log()
const clonedObj2 = clone(['someProperty','someNestedObj','someOtherProperty'], nestedObj);
console.log(clonedObj2 === nestedObj) // returns false
console.log(clonedObj2.someNestedObj === nestedObj.someNestedObj) //returns false
//returns true (doesn't attempt to clone because its primitive type)
console.log(clonedObj2.someNestedObj.someOtherProperty === nestedObj.someNestedObj.someOtherProperty)
Something like this might suffice,
const isObject = (thing) => {
if(thing &&
typeof thing === 'object' &&
typeof thing !== null
&& !(Array.isArray(thing))
){
return true;
}
return false;
}
/*
Call with an array containing the path to the property you want to access
And the current component/redux state.
For example if we want to update `hello` within the following obj
const obj = {
somePrimitive:false,
someNestedObj:{
hello:1
}
}
we would do :
//clone the object
const cloned = clone(['someNestedObj','hello'],obj)
//Set the new value
cloned.someNestedObj.hello = 5;
*/
const clone = (arr, state) => {
let clonedObj = {...state}
const originalObj = clonedObj;
arr.forEach(property => {
if(!(property in clonedObj)){
throw new Error('State missing property')
}
if(isObject(clonedObj[property])){
clonedObj[property] = {...originalObj[property]};
clonedObj = clonedObj[property];
}
})
return originalObj;
}
const nestedObj = {
someProperty:true,
someNestedObj:{
someOtherProperty:true
}
}
const clonedObj = clone(['someProperty'], nestedObj);
console.log(clonedObj === nestedObj) //returns false
console.log(clonedObj.someProperty === nestedObj.someProperty) //returns true
console.log(clonedObj.someNestedObj === nestedObj.someNestedObj) //returns true
console.log()
const clonedObj2 = clone(['someProperty','someNestedObj','someOtherProperty'], nestedObj);
console.log(clonedObj2 === nestedObj) // returns false
console.log(clonedObj2.someNestedObj === nestedObj.someNestedObj) //returns false
//returns true (doesn't attempt to clone because its primitive type)
console.log(clonedObj2.someNestedObj.someOtherProperty === nestedObj.someNestedObj.someOtherProperty)
answered Aug 29 at 4:28
Eladian
485316
485316
add a comment |
add a comment |
up vote
0
down vote
I take very seriously the concerns already voiced around creating a complete copy of your component state. That being said, if compromised performance is not of paramount concern (i.e. you don't have a very deep tree of components which React would want to re-render) I would suggest Immer.
import produce from 'immer';
<Input
value={this.state.form.username}
onChange={e => produce(this.state, s => { s.form.username = e.target.value }) } />
This should work for React.PureComponent
(i.e. shallow state comparisons by React) as Immer
cleverly uses a proxy object to efficiently copy an arbitrarily deep state tree. Immer is also more typesafe compared to libraries like Immutability Helper, and is ideal for Javascript and Typescript users alike.
Typescript utility function
function setStateDeep<S>(comp: React.Component<any, S, any>, fn: (s:
Draft<Readonly<S>>) => any) {
comp.setState(produce(comp.state, s => { fn(s); }))
}
onChange={e => setStateDeep(this, s => s.form.username = e.target.value)}
add a comment |
up vote
0
down vote
I take very seriously the concerns already voiced around creating a complete copy of your component state. That being said, if compromised performance is not of paramount concern (i.e. you don't have a very deep tree of components which React would want to re-render) I would suggest Immer.
import produce from 'immer';
<Input
value={this.state.form.username}
onChange={e => produce(this.state, s => { s.form.username = e.target.value }) } />
This should work for React.PureComponent
(i.e. shallow state comparisons by React) as Immer
cleverly uses a proxy object to efficiently copy an arbitrarily deep state tree. Immer is also more typesafe compared to libraries like Immutability Helper, and is ideal for Javascript and Typescript users alike.
Typescript utility function
function setStateDeep<S>(comp: React.Component<any, S, any>, fn: (s:
Draft<Readonly<S>>) => any) {
comp.setState(produce(comp.state, s => { fn(s); }))
}
onChange={e => setStateDeep(this, s => s.form.username = e.target.value)}
add a comment |
up vote
0
down vote
up vote
0
down vote
I take very seriously the concerns already voiced around creating a complete copy of your component state. That being said, if compromised performance is not of paramount concern (i.e. you don't have a very deep tree of components which React would want to re-render) I would suggest Immer.
import produce from 'immer';
<Input
value={this.state.form.username}
onChange={e => produce(this.state, s => { s.form.username = e.target.value }) } />
This should work for React.PureComponent
(i.e. shallow state comparisons by React) as Immer
cleverly uses a proxy object to efficiently copy an arbitrarily deep state tree. Immer is also more typesafe compared to libraries like Immutability Helper, and is ideal for Javascript and Typescript users alike.
Typescript utility function
function setStateDeep<S>(comp: React.Component<any, S, any>, fn: (s:
Draft<Readonly<S>>) => any) {
comp.setState(produce(comp.state, s => { fn(s); }))
}
onChange={e => setStateDeep(this, s => s.form.username = e.target.value)}
I take very seriously the concerns already voiced around creating a complete copy of your component state. That being said, if compromised performance is not of paramount concern (i.e. you don't have a very deep tree of components which React would want to re-render) I would suggest Immer.
import produce from 'immer';
<Input
value={this.state.form.username}
onChange={e => produce(this.state, s => { s.form.username = e.target.value }) } />
This should work for React.PureComponent
(i.e. shallow state comparisons by React) as Immer
cleverly uses a proxy object to efficiently copy an arbitrarily deep state tree. Immer is also more typesafe compared to libraries like Immutability Helper, and is ideal for Javascript and Typescript users alike.
Typescript utility function
function setStateDeep<S>(comp: React.Component<any, S, any>, fn: (s:
Draft<Readonly<S>>) => any) {
comp.setState(produce(comp.state, s => { fn(s); }))
}
onChange={e => setStateDeep(this, s => s.form.username = e.target.value)}
edited Nov 13 at 7:51
answered Nov 13 at 7:02
Stephen Paul
14.3k84644
14.3k84644
add a comment |
add a comment |
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f43040721%2fhow-to-update-nested-state-properties-in-react%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
2
What do you mean doesn't work? This is not a very good question - what happened? Any errors? What errors?
– Darren Sweeney
Mar 27 '17 at 7:53
Try to read: stackoverflow.com/questions/18933985/…
– Andrew Paramoshkin
Mar 27 '17 at 7:58
4
The answer is Don't use Nested State in React. Read this excellent answer.
– Qwerty
Aug 10 at 8:57
nested state is bad programming style in React JS
– Troyan Victor
Oct 5 at 8:30