How to update nested state properties in React
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 |
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 '18 at 8:57
nested state is bad programming style in React JS
– Troyan Victor
Oct 5 '18 at 8:30
add a comment |
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 '18 at 0:29
Dan Dascalescu
66.5k21201274
66.5k21201274
asked Mar 27 '17 at 7:51
Alex YongAlex Yong
1,0552721
1,0552721
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 '18 at 8:57
nested state is bad programming style in React JS
– Troyan Victor
Oct 5 '18 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 '18 at 8:57
nested state is bad programming style in React JS
– Troyan Victor
Oct 5 '18 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 '18 at 8:57
The answer is Don't use Nested State in React. Read this excellent answer.
– Qwerty
Aug 10 '18 at 8:57
nested state is bad programming style in React JS
– Troyan Victor
Oct 5 '18 at 8:30
nested state is bad programming style in React JS
– Troyan Victor
Oct 5 '18 at 8:30
add a comment |
16 Answers
16
active
oldest
votes
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
5
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 '18 at 19:43
|
show 7 more comments
To write it in one line
this.setState({ someProperty: { ...this.state.someProperty, flag: false} });
6
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 '18 at 12:52
2
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 '18 at 13:06
1
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 '18 at 4:27
add a comment |
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 '18 at 13:45
Shallow comparison is used for pure components by default.
– Basel Shishani
Sep 26 '18 at 12:10
1
I want to create an altar in your honor and pray to it every morning. It took me three days to reach this answer that perfectly explains the OBVIOUS design decision. Everyone just tries to use the spread operator or do other hacks just because nested state looks more human-readable.
– Edeph
Nov 29 '18 at 14:06
add a comment |
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.
7
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 '18 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 '18 at 9:44
Also I added an example react-experiments.herokuapp.com/state-flow (link to git at the top)
– Qwerty
Aug 9 '18 at 16:28
What if you load for example an object with nested attributes from a database into state? How to avoid nested state in that case?
– tobiv
Jan 7 at 0:17
1
@tobiv It's better to transform the data upon a fetch into a different structure, but it depends on the usecase. It's okay to have a nested object or array of objects in a state if you think of them as compact units of some data. The problem arises when you need to store UI switches or other observable data (i.e. data that should immediately change the view) as they must be flat in order to correctly trigger internal React diffing algorithms. For changes in other nested objects, it's easy to triggerthis.forceUpdate()
or implement specificshouldComponentUpdate
.
– Qwerty
Jan 7 at 17:44
add a comment |
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.
9
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 |
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 '18 at 20:37
@YixingLiu: I've updated the answer to uselodash/fp
, so we don't have to worry about mutations.
– tokland
Jul 26 '18 at 20:50
add a comment |
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 |
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 |
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 |
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 |
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 |
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 |
I take very seriously the concerns already voiced around creating a complete copy of your component state. With that said, I would strongly 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 |
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 |
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 |
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 |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
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
16 Answers
16
active
oldest
votes
16 Answers
16
active
oldest
votes
active
oldest
votes
active
oldest
votes
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
5
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 '18 at 19:43
|
show 7 more comments
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
5
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 '18 at 19:43
|
show 7 more comments
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 '18 at 18:55
Bastiaan ten Klooster
13812
13812
answered Mar 27 '17 at 8:25
Shubham KhatriShubham Khatri
81.2k1598137
81.2k1598137
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
5
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 '18 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
5
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 '18 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
5
5
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 '18 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 '18 at 19:43
|
show 7 more comments
To write it in one line
this.setState({ someProperty: { ...this.state.someProperty, flag: false} });
6
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 '18 at 12:52
2
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 '18 at 13:06
1
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 '18 at 4:27
add a comment |
To write it in one line
this.setState({ someProperty: { ...this.state.someProperty, flag: false} });
6
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 '18 at 12:52
2
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 '18 at 13:06
1
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 '18 at 4:27
add a comment |
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
YosephYoseph
1,128813
1,128813
6
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 '18 at 12:52
2
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 '18 at 13:06
1
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 '18 at 4:27
add a comment |
6
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 '18 at 12:52
2
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 '18 at 13:06
1
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 '18 at 4:27
6
6
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 '18 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 '18 at 12:52
2
2
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 '18 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 '18 at 13:06
1
1
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 '18 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 '18 at 4:27
add a comment |
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 '18 at 13:45
Shallow comparison is used for pure components by default.
– Basel Shishani
Sep 26 '18 at 12:10
1
I want to create an altar in your honor and pray to it every morning. It took me three days to reach this answer that perfectly explains the OBVIOUS design decision. Everyone just tries to use the spread operator or do other hacks just because nested state looks more human-readable.
– Edeph
Nov 29 '18 at 14:06
add a comment |
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 '18 at 13:45
Shallow comparison is used for pure components by default.
– Basel Shishani
Sep 26 '18 at 12:10
1
I want to create an altar in your honor and pray to it every morning. It took me three days to reach this answer that perfectly explains the OBVIOUS design decision. Everyone just tries to use the spread operator or do other hacks just because nested state looks more human-readable.
– Edeph
Nov 29 '18 at 14:06
add a comment |
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 '18 at 12:18
Konstantin SmolyaninKonstantin Smolyanin
11.4k54132
11.4k54132
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 '18 at 13:45
Shallow comparison is used for pure components by default.
– Basel Shishani
Sep 26 '18 at 12:10
1
I want to create an altar in your honor and pray to it every morning. It took me three days to reach this answer that perfectly explains the OBVIOUS design decision. Everyone just tries to use the spread operator or do other hacks just because nested state looks more human-readable.
– Edeph
Nov 29 '18 at 14:06
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 '18 at 13:45
Shallow comparison is used for pure components by default.
– Basel Shishani
Sep 26 '18 at 12:10
1
I want to create an altar in your honor and pray to it every morning. It took me three days to reach this answer that perfectly explains the OBVIOUS design decision. Everyone just tries to use the spread operator or do other hacks just because nested state looks more human-readable.
– Edeph
Nov 29 '18 at 14:06
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 '18 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 '18 at 13:45
Shallow comparison is used for pure components by default.
– Basel Shishani
Sep 26 '18 at 12:10
Shallow comparison is used for pure components by default.
– Basel Shishani
Sep 26 '18 at 12:10
1
1
I want to create an altar in your honor and pray to it every morning. It took me three days to reach this answer that perfectly explains the OBVIOUS design decision. Everyone just tries to use the spread operator or do other hacks just because nested state looks more human-readable.
– Edeph
Nov 29 '18 at 14:06
I want to create an altar in your honor and pray to it every morning. It took me three days to reach this answer that perfectly explains the OBVIOUS design decision. Everyone just tries to use the spread operator or do other hacks just because nested state looks more human-readable.
– Edeph
Nov 29 '18 at 14:06
add a comment |
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.
7
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 '18 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 '18 at 9:44
Also I added an example react-experiments.herokuapp.com/state-flow (link to git at the top)
– Qwerty
Aug 9 '18 at 16:28
What if you load for example an object with nested attributes from a database into state? How to avoid nested state in that case?
– tobiv
Jan 7 at 0:17
1
@tobiv It's better to transform the data upon a fetch into a different structure, but it depends on the usecase. It's okay to have a nested object or array of objects in a state if you think of them as compact units of some data. The problem arises when you need to store UI switches or other observable data (i.e. data that should immediately change the view) as they must be flat in order to correctly trigger internal React diffing algorithms. For changes in other nested objects, it's easy to triggerthis.forceUpdate()
or implement specificshouldComponentUpdate
.
– Qwerty
Jan 7 at 17:44
add a comment |
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.
7
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 '18 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 '18 at 9:44
Also I added an example react-experiments.herokuapp.com/state-flow (link to git at the top)
– Qwerty
Aug 9 '18 at 16:28
What if you load for example an object with nested attributes from a database into state? How to avoid nested state in that case?
– tobiv
Jan 7 at 0:17
1
@tobiv It's better to transform the data upon a fetch into a different structure, but it depends on the usecase. It's okay to have a nested object or array of objects in a state if you think of them as compact units of some data. The problem arises when you need to store UI switches or other observable data (i.e. data that should immediately change the view) as they must be flat in order to correctly trigger internal React diffing algorithms. For changes in other nested objects, it's easy to triggerthis.forceUpdate()
or implement specificshouldComponentUpdate
.
– Qwerty
Jan 7 at 17:44
add a comment |
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 '18 at 10:04
answered May 3 '18 at 7:50
QwertyQwerty
9,27775271
9,27775271
7
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 '18 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 '18 at 9:44
Also I added an example react-experiments.herokuapp.com/state-flow (link to git at the top)
– Qwerty
Aug 9 '18 at 16:28
What if you load for example an object with nested attributes from a database into state? How to avoid nested state in that case?
– tobiv
Jan 7 at 0:17
1
@tobiv It's better to transform the data upon a fetch into a different structure, but it depends on the usecase. It's okay to have a nested object or array of objects in a state if you think of them as compact units of some data. The problem arises when you need to store UI switches or other observable data (i.e. data that should immediately change the view) as they must be flat in order to correctly trigger internal React diffing algorithms. For changes in other nested objects, it's easy to triggerthis.forceUpdate()
or implement specificshouldComponentUpdate
.
– Qwerty
Jan 7 at 17:44
add a comment |
7
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 '18 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 '18 at 9:44
Also I added an example react-experiments.herokuapp.com/state-flow (link to git at the top)
– Qwerty
Aug 9 '18 at 16:28
What if you load for example an object with nested attributes from a database into state? How to avoid nested state in that case?
– tobiv
Jan 7 at 0:17
1
@tobiv It's better to transform the data upon a fetch into a different structure, but it depends on the usecase. It's okay to have a nested object or array of objects in a state if you think of them as compact units of some data. The problem arises when you need to store UI switches or other observable data (i.e. data that should immediately change the view) as they must be flat in order to correctly trigger internal React diffing algorithms. For changes in other nested objects, it's easy to triggerthis.forceUpdate()
or implement specificshouldComponentUpdate
.
– Qwerty
Jan 7 at 17:44
7
7
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 '18 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 '18 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 '18 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 '18 at 9:44
Also I added an example react-experiments.herokuapp.com/state-flow (link to git at the top)
– Qwerty
Aug 9 '18 at 16:28
Also I added an example react-experiments.herokuapp.com/state-flow (link to git at the top)
– Qwerty
Aug 9 '18 at 16:28
What if you load for example an object with nested attributes from a database into state? How to avoid nested state in that case?
– tobiv
Jan 7 at 0:17
What if you load for example an object with nested attributes from a database into state? How to avoid nested state in that case?
– tobiv
Jan 7 at 0:17
1
1
@tobiv It's better to transform the data upon a fetch into a different structure, but it depends on the usecase. It's okay to have a nested object or array of objects in a state if you think of them as compact units of some data. The problem arises when you need to store UI switches or other observable data (i.e. data that should immediately change the view) as they must be flat in order to correctly trigger internal React diffing algorithms. For changes in other nested objects, it's easy to trigger
this.forceUpdate()
or implement specific shouldComponentUpdate
.– Qwerty
Jan 7 at 17:44
@tobiv It's better to transform the data upon a fetch into a different structure, but it depends on the usecase. It's okay to have a nested object or array of objects in a state if you think of them as compact units of some data. The problem arises when you need to store UI switches or other observable data (i.e. data that should immediately change the view) as they must be flat in order to correctly trigger internal React diffing algorithms. For changes in other nested objects, it's easy to trigger
this.forceUpdate()
or implement specific shouldComponentUpdate
.– Qwerty
Jan 7 at 17:44
add a comment |
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.
9
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 |
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.
9
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 |
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 '18 at 6:58
answered Oct 7 '17 at 19:20
Alyssa RooseAlyssa Roose
19915
19915
9
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 |
9
And I could be wrong but I don't think you're using React without ES2015.
– Madbreaks
Nov 10 '17 at 18:29
9
9
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 |
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 '18 at 20:37
@YixingLiu: I've updated the answer to uselodash/fp
, so we don't have to worry about mutations.
– tokland
Jul 26 '18 at 20:50
add a comment |
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 '18 at 20:37
@YixingLiu: I've updated the answer to uselodash/fp
, so we don't have to worry about mutations.
– tokland
Jul 26 '18 at 20:50
add a comment |
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 '18 at 18:42
answered May 19 '17 at 8:15
toklandtokland
51.8k11115143
51.8k11115143
If you uselodash
, you probably want to try_.cloneDeep
and set state to be the cloned copy.
– Yixing Liu
Jul 26 '18 at 20:37
@YixingLiu: I've updated the answer to uselodash/fp
, so we don't have to worry about mutations.
– tokland
Jul 26 '18 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 '18 at 20:37
@YixingLiu: I've updated the answer to uselodash/fp
, so we don't have to worry about mutations.
– tokland
Jul 26 '18 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 '18 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 '18 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 '18 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 '18 at 20:50
add a comment |
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 |
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 |
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 BerkompasMatthew Berkompas
6112
6112
add a comment |
add a comment |
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 |
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 |
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 '18 at 13:30
enzolitoenzolito
7817
7817
add a comment |
add a comment |
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 |
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 |
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 '18 at 9:40
Joakim JäderbergJoakim Jäderberg
311
311
add a comment |
add a comment |
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 |
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 |
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 '18 at 9:40
Alberto PirasAlberto Piras
9113
9113
add a comment |
add a comment |
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 |
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 |
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 '18 at 2:57
answered Jun 28 '18 at 2:51
Cory HouseCory House
7,95275776
7,95275776
add a comment |
add a comment |
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 |
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 |
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 '18 at 9:51
DhakadDhakad
154
154
add a comment |
add a comment |
I take very seriously the concerns already voiced around creating a complete copy of your component state. With that said, I would strongly 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 |
I take very seriously the concerns already voiced around creating a complete copy of your component state. With that said, I would strongly 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 |
I take very seriously the concerns already voiced around creating a complete copy of your component state. With that said, I would strongly 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. With that said, I would strongly 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 yesterday
answered Nov 13 '18 at 7:02
Stephen PaulStephen Paul
15.2k84845
15.2k84845
add a comment |
add a comment |
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 |
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 |
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 '18 at 15:36
Michael StokesMichael Stokes
153314
153314
add a comment |
add a comment |
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 |
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 |
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 '18 at 8:29
VenugopalVenugopal
1,63711328
1,63711328
add a comment |
add a comment |
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 |
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 |
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 '18 at 4:28
EladianEladian
492316
492316
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
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 '18 at 8:57
nested state is bad programming style in React JS
– Troyan Victor
Oct 5 '18 at 8:30