How to update nested state properties in React











up vote
115
down vote

favorite
44












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?










share|improve this question




















  • 2




    What do you mean doesn't work? This is not a very good question - what happened? Any errors? What errors?
    – Darren Sweeney
    Mar 27 '17 at 7:53










  • Try to read: stackoverflow.com/questions/18933985/…
    – Andrew Paramoshkin
    Mar 27 '17 at 7:58






  • 4




    The answer is Don't use Nested State in React. Read this excellent answer.
    – Qwerty
    Aug 10 at 8:57












  • nested state is bad programming style in React JS
    – Troyan Victor
    Oct 5 at 8:30















up vote
115
down vote

favorite
44












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?










share|improve this question




















  • 2




    What do you mean doesn't work? This is not a very good question - what happened? Any errors? What errors?
    – Darren Sweeney
    Mar 27 '17 at 7:53










  • Try to read: stackoverflow.com/questions/18933985/…
    – Andrew Paramoshkin
    Mar 27 '17 at 7:58






  • 4




    The answer is Don't use Nested State in React. Read this excellent answer.
    – Qwerty
    Aug 10 at 8:57












  • nested state is bad programming style in React JS
    – Troyan Victor
    Oct 5 at 8:30













up vote
115
down vote

favorite
44









up vote
115
down vote

favorite
44






44





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?










share|improve this question















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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Jul 30 at 0:29









Dan Dascalescu

64.3k20195270




64.3k20195270










asked Mar 27 '17 at 7:51









Alex Yong

9202719




9202719








  • 2




    What do you mean doesn't work? This is not a very good question - what happened? Any errors? What errors?
    – Darren Sweeney
    Mar 27 '17 at 7:53










  • Try to read: stackoverflow.com/questions/18933985/…
    – Andrew Paramoshkin
    Mar 27 '17 at 7:58






  • 4




    The answer is Don't use Nested State in React. Read this excellent answer.
    – Qwerty
    Aug 10 at 8:57












  • nested state is bad programming style in React JS
    – Troyan Victor
    Oct 5 at 8:30














  • 2




    What do you mean doesn't work? This is not a very good question - what happened? Any errors? What errors?
    – Darren Sweeney
    Mar 27 '17 at 7:53










  • Try to read: stackoverflow.com/questions/18933985/…
    – Andrew Paramoshkin
    Mar 27 '17 at 7:58






  • 4




    The answer is Don't use Nested State in React. Read this excellent answer.
    – Qwerty
    Aug 10 at 8:57












  • nested state is bad programming style in React JS
    – Troyan Victor
    Oct 5 at 8:30








2




2




What do you mean doesn't work? This is not a very good question - what happened? Any errors? What errors?
– Darren Sweeney
Mar 27 '17 at 7:53




What do you mean doesn't work? This is not a very good question - what happened? Any errors? What errors?
– Darren Sweeney
Mar 27 '17 at 7:53












Try to read: stackoverflow.com/questions/18933985/…
– Andrew Paramoshkin
Mar 27 '17 at 7:58




Try to read: stackoverflow.com/questions/18933985/…
– Andrew Paramoshkin
Mar 27 '17 at 7:58




4




4




The answer is Don't use Nested State in React. Read this excellent answer.
– Qwerty
Aug 10 at 8:57






The answer is Don't use Nested State in React. Read this excellent answer.
– Qwerty
Aug 10 at 8:57














nested state is bad programming style in React JS
– Troyan Victor
Oct 5 at 8:30




nested state is bad programming style in React JS
– Troyan Victor
Oct 5 at 8:30












16 Answers
16






active

oldest

votes

















up vote
164
down vote



accepted










In order to setState for a nested object you can follow the below approach as I think setState doesn't handle nested updates.



var someProperty = {...this.state.someProperty}
someProperty.flag = true;
this.setState({someProperty})


The idea is to create a dummy object perform operations on it and then replace the component's state with the updated object



Now, the spread operator creates only one level nested copy of the object. If your state is highly nested like:



this.state = {
someProperty: {
someOtherProperty: {
anotherProperty: {
flag: true
}
..
}
...
}
...
}


You could setState using spread operator at each level like



this.setState(prevState => ({
...prevState,
someProperty: {
...prevState.someProperty,
someOtherProperty: {
...prevState.someProperty.someOtherProperty,
anotherProperty: {
...prevState.someProperty.someOtherProperty.anotherProperty,
flag: false
}
}
}
}))


However the above syntax get every ugly as the state becomes more and more nested and hence I recommend you to use immutability-helper package to update the state.



See this answer on how to update state with immutability helper.






share|improve this answer



















  • 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






  • 2




    this didn't work for me.. i'm using react version @15.6.1
    – Rajiv
    Jun 30 '17 at 20:36






  • 2




    @Stophface We can use Lodash to deep clone sure, but not everyone would include this library just to setState
    – Shubham Khatri
    Nov 23 '17 at 5:36






  • 3




    Doesn't this violate reactjs.org/docs/… ? If two changes to the nested object got batched the last change would overwrite the first. So the most correct way would be: this.setState((prevState) => ({nested: {...prevState.nested, propertyToSet: newValue}}) A bit tedious though...
    – olejorgenb
    Dec 21 '17 at 20:55






  • 1




    I don't think you need the ...prevState, in the last example, you only someProperty and its children
    – Jemar Jones
    May 25 at 19:43


















up vote
57
down vote













To write it in one line



this.setState({ someProperty: { ...this.state.someProperty, flag: false} });





share|improve this answer

















  • 3




    It is recommended to use the updater for setState instead of directly accessing this.state within setState. reactjs.org/docs/react-component.html#setstate
    – Raghu Teja
    May 23 at 12:52










  • I believe that's saying, don't read this.state immediately after setState and expect it to have the new value. Versus calling this.state within setState. Or is there also an issue with the latter that isn't clear to me?
    – trpt4him
    Jun 1 at 13:06










  • As trpt4him said, the link you gave talks about the problem of accessing this.state after setState. Besides, we are not passing this.state to setState. The operator spread properties. It is the same as Object.assign(). github.com/tc39/proposal-object-rest-spread
    – Yoseph
    Jun 4 at 4:27




















up vote
23
down vote













Sometimes direct answers are not the best ones :)



Short version:



this code



this.state = {
someProperty: {
flag: true
}
}


should be simplified as something like



this.state = {
somePropertyFlag: true
}


Long version:



Currently you shouldn't want to work with nested state in React. Because React is not oriented to work with nested states and all solutions proposed here look as hacks. They don't use the framework but fight with it. They suggest to write not so clear code for doubtful purpose of grouping some properties. So they are very interesting as an answer to the challenge but practically useless.



Lets imagine the following state:



{
parent: {
child1: 'value 1',
child2: 'value 2',
...
child100: 'value 100'
}
}


What will happen if you change just a value of child1? React will not re-render the view because it uses shallow comparison and it will find that parent property didn't change. BTW mutating the state object directly is considered to be a bad practice in general.



So you need to re-create the whole parent object. But in this case we will meet another problem. React will think that all children have changed their values and will re-render all of them. Of course it is not good for performance.



It is still possible to solve that problem by writing some complicated logic in shouldComponentUpdate() but I would prefer to stop here and use simple solution from the short version.






share|improve this answer





















  • I think there are use cases where a nested state is perfectly fine. E.g. a state dynamically keeping track of key-value pairs. See here how you can update the state for just a single entry in the state: reactjs.org/docs/…
    – pors
    Aug 30 at 13:45










  • Shallow comparison is used for pure components by default.
    – Basel Shishani
    Sep 26 at 12:10


















up vote
18
down vote













If you are using ES2015 you have access to the Object.assign. You can use it as follows to update a nested object.



this.setState({
someProperty: Object.assign({}, this.state.someProperty, {flag: false})
});


You merge the updated properties with the existing and use the returned object to update the state.



Edit: Added an empty object as target to the assign function to make sure the state isn't mutated directly as carkod pointed out.






share|improve this answer



















  • 8




    And I could be wrong but I don't think you're using React without ES2015.
    – Madbreaks
    Nov 10 '17 at 18:29




















up vote
12
down vote













Disclaimer




Nested State in React is wrong design



Read this excellent answer.




 




Reasoning behind this answer:



React's setState is just a built-in convenience, but you soon realise
that it has its limits. Using custom properties and intelligent use of
forceUpdate gives you much more.
eg:



class MyClass extends React.Component {
myState = someObject
inputValue = 42
...


MobX, for example, ditches state completely and uses custom observable properties.
Use Observables instead of state in React components.




 





the best answer of all - see example here



There is another shorter way to update whatever nested property.



this.setState(state => {
state.nested.flag = false
state.another.deep.prop = true
return state
})


On one line




this.setState(state => (state.nested.flag = false, state))



Of course this is abusing some core principles, as the state should be read-only, but since you are immediately discarding the old state and replacing it with new state, it is completely ok.



Warning



Even though the component containing the state will update and rerender properly (except this gotcha), the props will fail to propagate to children (see Spymaster's comment below). Only use this technique if you know what you are doing.



For example, you may pass a changed flat prop that is updated and passed easily.



render(
//some complex render with your nested state
<ChildComponent complexNestedProp={this.state.nested} pleaseRerender={Math.random()}/>
)


Now even though reference for complexNestedProp did not change (shouldComponentUpdate)



this.props.complexNestedProp === nextProps.complexNestedProp


the component will rerender whenever parent component updates, which is the case after calling this.setState or this.forceUpdate in the parent.






share|improve this answer



















  • 6




    You shouldn't be mutating any react state anywhere. If there are nested components that use data from state.nested or state.another, they won't re-render nor will their children. This is because the object did not change, thus React thinks nothing has changed.
    – SpyMaster356
    May 12 at 16:51










  • @SpyMaster356 I have updated my answer to address this. Also note that MobX, for example, ditches state completely and uses custom observable properties. React's setState is just a built-in convenience, but you soon realise that it has its limits. Using custom properties and intelligent use of forceUpdate gives you much more. Use Observables instead of state in React components.
    – Qwerty
    Aug 9 at 9:44












  • Also I added an example react-experiments.herokuapp.com/state-flow (link to git at the top)
    – Qwerty
    Aug 9 at 16:28


















up vote
6
down vote













There are many libraries to help with this. For example, using immutability-helper:



import update from 'immutability-helper';

const newState = update(this.state, {
someProperty: {flag: {$set: false}},
};
this.setState(newState);


Or using lodash/fp:



import {merge} from 'lodash/fp';

const newState = merge(this.state, {
someProperty: {flag: false},
});





share|improve this answer























  • If you use lodash, you probably want to try _.cloneDeep and set state to be the cloned copy.
    – Yixing Liu
    Jul 26 at 20:37










  • @YixingLiu: I've updated the answer to use lodash/fp, so we don't have to worry about mutations.
    – tokland
    Jul 26 at 20:50




















up vote
5
down vote













Here's a variation on the first answer given in this thread which doesn't require any extra packages, libraries or special functions.



state = {
someProperty: {
flag: 'string'
}
}

handleChange = (value) => {
const newState = {...this.state.someProperty, flag: value}
this.setState({ someProperty: newState })
}


In order to set the state of a specific nested field, you have set the whole object. I did this by creating a variable, newState and spreading the contents of the current state into it first using the ES2015 spread operator. Then, I replaced the value of this.state.flag with the new value (since I set flag: value after I spread the current state into the object, the flag field in the current state is overridden). Then, I simply set the state of someProperty to my newState object.






share|improve this answer




























    up vote
    5
    down vote













    You can also go this way (which feels more readable to me):



    const newState = Object.assign({}, this.state);
    newState.property.nestedProperty = 'newValue';
    this.setState(newState);





    share|improve this answer




























      up vote
      2
      down vote













      I used this solution.



      If you have a nested state like this:



         this.state = {
      formInputs:{
      friendName:{
      value:'',
      isValid:false,
      errorMsg:''
      },
      friendEmail:{
      value:'',
      isValid:false,
      errorMsg:''
      }
      }


      you can declare the handleChange function that copy current status and re-assigns it with changed values



      handleChange(el) {
      let inputName = el.target.name;
      let inputValue = el.target.value;

      let statusCopy = Object.assign({}, this.state);
      statusCopy.formInputs[inputName].value = inputValue;

      this.setState(statusCopy);
      }


      here the html with the event listener



      <input type="text" onChange={this.handleChange} " name="friendName" />





      share|improve this answer




























        up vote
        2
        down vote













        Two other options not mentioned yet:




        1. 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.

        2. 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.






        share|improve this answer






























          up vote
          1
          down vote













          To make things generic, I worked on @ShubhamKhatri's and @Qwerty's answers.



          state object



          this.state = {
          name: '',
          grandParent: {
          parent1: {
          child: ''
          },
          parent2: {
          child: ''
          }
          }
          };


          input controls



          <input
          value={this.state.name}
          onChange={this.updateState}
          type="text"
          name="name"
          />
          <input
          value={this.state.grandParent.parent1.child}
          onChange={this.updateState}
          type="text"
          name="grandParent.parent1.child"
          />
          <input
          value={this.state.grandParent.parent2.child}
          onChange={this.updateState}
          type="text"
          name="grandParent.parent2.child"
          />


          updateState method



          setState as @ShubhamKhatri's answer



          updateState(event) {
          const path = event.target.name.split('.');
          const depth = path.length;
          const oldstate = this.state;
          const newstate = { ...oldstate };
          let newStateLevel = newstate;
          let oldStateLevel = oldstate;

          for (let i = 0; i < depth; i += 1) {
          if (i === depth - 1) {
          newStateLevel[path[i]] = event.target.value;
          } else {
          newStateLevel[path[i]] = { ...oldStateLevel[path[i]] };
          oldStateLevel = oldStateLevel[path[i]];
          newStateLevel = newStateLevel[path[i]];
          }
          }
          this.setState(newstate);
          }


          setState as @Qwerty's answer



          updateState(event) {
          const path = event.target.name.split('.');
          const depth = path.length;
          const state = { ...this.state };
          let ref = state;
          for (let i = 0; i < depth; i += 1) {
          if (i === depth - 1) {
          ref[path[i]] = event.target.value;
          } else {
          ref = ref[path[i]];
          }
          }
          this.setState(state);
          }


          Note: These above methods won't work for arrays






          share|improve this answer




























            up vote
            1
            down vote













            We use Immer https://github.com/mweststrate/immer to handle these kinds of issues.



            Just replaced this code in one of our components



            this.setState(prevState => ({
            ...prevState,
            preferences: {
            ...prevState.preferences,
            [key]: newValue
            }
            }));


            With this



            import produce from 'immer';

            this.setState(produce(draft => {
            draft.preferences[key] = newValue;
            }));


            With immer you handle your state as a "normal object".
            The magic happens behind the scene with proxy objects.






            share|improve this answer




























              up vote
              1
              down vote













              Create a copy of the state:
              let someProperty = JSON.parse(JSON.stringify(this.state.someProperty))



              make changes in this object:
              someProperty.flag = "false"



              now update the state

              this.setState({someProperty})






              share|improve this answer




























                up vote
                0
                down vote













                I found this to work for me, having a project form in my case where for example you have an id, and a name and I'd rather maintain state for a nested project.



                return (
                <div>
                <h2>Project Details</h2>
                <form>
                <Input label="ID" group type="number" value={this.state.project.id} onChange={(event) => this.setState({ project: {...this.state.project, id: event.target.value}})} />
                <Input label="Name" group type="text" value={this.state.project.name} onChange={(event) => this.setState({ project: {...this.state.project, name: event.target.value}})} />
                </form>
                </div>
                )


                Let me know!






                share|improve this answer




























                  up vote
                  0
                  down vote













                  Something like this might suffice,



                  const isObject = (thing) => {
                  if(thing &&
                  typeof thing === 'object' &&
                  typeof thing !== null
                  && !(Array.isArray(thing))
                  ){
                  return true;
                  }
                  return false;
                  }

                  /*
                  Call with an array containing the path to the property you want to access
                  And the current component/redux state.

                  For example if we want to update `hello` within the following obj
                  const obj = {
                  somePrimitive:false,
                  someNestedObj:{
                  hello:1
                  }
                  }

                  we would do :
                  //clone the object
                  const cloned = clone(['someNestedObj','hello'],obj)
                  //Set the new value
                  cloned.someNestedObj.hello = 5;

                  */
                  const clone = (arr, state) => {
                  let clonedObj = {...state}
                  const originalObj = clonedObj;
                  arr.forEach(property => {
                  if(!(property in clonedObj)){
                  throw new Error('State missing property')
                  }

                  if(isObject(clonedObj[property])){
                  clonedObj[property] = {...originalObj[property]};
                  clonedObj = clonedObj[property];
                  }
                  })
                  return originalObj;
                  }

                  const nestedObj = {
                  someProperty:true,
                  someNestedObj:{
                  someOtherProperty:true
                  }
                  }

                  const clonedObj = clone(['someProperty'], nestedObj);
                  console.log(clonedObj === nestedObj) //returns false
                  console.log(clonedObj.someProperty === nestedObj.someProperty) //returns true
                  console.log(clonedObj.someNestedObj === nestedObj.someNestedObj) //returns true

                  console.log()
                  const clonedObj2 = clone(['someProperty','someNestedObj','someOtherProperty'], nestedObj);
                  console.log(clonedObj2 === nestedObj) // returns false
                  console.log(clonedObj2.someNestedObj === nestedObj.someNestedObj) //returns false
                  //returns true (doesn't attempt to clone because its primitive type)
                  console.log(clonedObj2.someNestedObj.someOtherProperty === nestedObj.someNestedObj.someOtherProperty)





                  share|improve this answer




























                    up vote
                    0
                    down vote













                    I take very seriously the concerns already voiced around creating a complete copy of your component state. That being said, if compromised performance is not of paramount concern (i.e. you don't have a very deep tree of components which React would want to re-render) I would suggest Immer.



                    import produce from 'immer';

                    <Input
                    value={this.state.form.username}
                    onChange={e => produce(this.state, s => { s.form.username = e.target.value }) } />


                    This should work for React.PureComponent (i.e. shallow state comparisons by React) as Immer cleverly uses a proxy object to efficiently copy an arbitrarily deep state tree. Immer is also more typesafe compared to libraries like Immutability Helper, and is ideal for Javascript and Typescript users alike.





                    Typescript utility function



                    function setStateDeep<S>(comp: React.Component<any, S, any>, fn: (s: 
                    Draft<Readonly<S>>) => any) {
                    comp.setState(produce(comp.state, s => { fn(s); }))
                    }

                    onChange={e => setStateDeep(this, s => s.form.username = e.target.value)}





                    share|improve this answer























                      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',
                      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
                      });


                      }
                      });














                       

                      draft saved


                      draft discarded


















                      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








                      up vote
                      164
                      down vote



                      accepted










                      In order to setState for a nested object you can follow the below approach as I think setState doesn't handle nested updates.



                      var someProperty = {...this.state.someProperty}
                      someProperty.flag = true;
                      this.setState({someProperty})


                      The idea is to create a dummy object perform operations on it and then replace the component's state with the updated object



                      Now, the spread operator creates only one level nested copy of the object. If your state is highly nested like:



                      this.state = {
                      someProperty: {
                      someOtherProperty: {
                      anotherProperty: {
                      flag: true
                      }
                      ..
                      }
                      ...
                      }
                      ...
                      }


                      You could setState using spread operator at each level like



                      this.setState(prevState => ({
                      ...prevState,
                      someProperty: {
                      ...prevState.someProperty,
                      someOtherProperty: {
                      ...prevState.someProperty.someOtherProperty,
                      anotherProperty: {
                      ...prevState.someProperty.someOtherProperty.anotherProperty,
                      flag: false
                      }
                      }
                      }
                      }))


                      However the above syntax get every ugly as the state becomes more and more nested and hence I recommend you to use immutability-helper package to update the state.



                      See this answer on how to update state with immutability helper.






                      share|improve this answer



















                      • 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






                      • 2




                        this didn't work for me.. i'm using react version @15.6.1
                        – Rajiv
                        Jun 30 '17 at 20:36






                      • 2




                        @Stophface We can use Lodash to deep clone sure, but not everyone would include this library just to setState
                        – Shubham Khatri
                        Nov 23 '17 at 5:36






                      • 3




                        Doesn't this violate reactjs.org/docs/… ? If two changes to the nested object got batched the last change would overwrite the first. So the most correct way would be: this.setState((prevState) => ({nested: {...prevState.nested, propertyToSet: newValue}}) A bit tedious though...
                        – olejorgenb
                        Dec 21 '17 at 20:55






                      • 1




                        I don't think you need the ...prevState, in the last example, you only someProperty and its children
                        – Jemar Jones
                        May 25 at 19:43















                      up vote
                      164
                      down vote



                      accepted










                      In order to setState for a nested object you can follow the below approach as I think setState doesn't handle nested updates.



                      var someProperty = {...this.state.someProperty}
                      someProperty.flag = true;
                      this.setState({someProperty})


                      The idea is to create a dummy object perform operations on it and then replace the component's state with the updated object



                      Now, the spread operator creates only one level nested copy of the object. If your state is highly nested like:



                      this.state = {
                      someProperty: {
                      someOtherProperty: {
                      anotherProperty: {
                      flag: true
                      }
                      ..
                      }
                      ...
                      }
                      ...
                      }


                      You could setState using spread operator at each level like



                      this.setState(prevState => ({
                      ...prevState,
                      someProperty: {
                      ...prevState.someProperty,
                      someOtherProperty: {
                      ...prevState.someProperty.someOtherProperty,
                      anotherProperty: {
                      ...prevState.someProperty.someOtherProperty.anotherProperty,
                      flag: false
                      }
                      }
                      }
                      }))


                      However the above syntax get every ugly as the state becomes more and more nested and hence I recommend you to use immutability-helper package to update the state.



                      See this answer on how to update state with immutability helper.






                      share|improve this answer



















                      • 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






                      • 2




                        this didn't work for me.. i'm using react version @15.6.1
                        – Rajiv
                        Jun 30 '17 at 20:36






                      • 2




                        @Stophface We can use Lodash to deep clone sure, but not everyone would include this library just to setState
                        – Shubham Khatri
                        Nov 23 '17 at 5:36






                      • 3




                        Doesn't this violate reactjs.org/docs/… ? If two changes to the nested object got batched the last change would overwrite the first. So the most correct way would be: this.setState((prevState) => ({nested: {...prevState.nested, propertyToSet: newValue}}) A bit tedious though...
                        – olejorgenb
                        Dec 21 '17 at 20:55






                      • 1




                        I don't think you need the ...prevState, in the last example, you only someProperty and its children
                        – Jemar Jones
                        May 25 at 19:43













                      up vote
                      164
                      down vote



                      accepted







                      up vote
                      164
                      down vote



                      accepted






                      In order to setState for a nested object you can follow the below approach as I think setState doesn't handle nested updates.



                      var someProperty = {...this.state.someProperty}
                      someProperty.flag = true;
                      this.setState({someProperty})


                      The idea is to create a dummy object perform operations on it and then replace the component's state with the updated object



                      Now, the spread operator creates only one level nested copy of the object. If your state is highly nested like:



                      this.state = {
                      someProperty: {
                      someOtherProperty: {
                      anotherProperty: {
                      flag: true
                      }
                      ..
                      }
                      ...
                      }
                      ...
                      }


                      You could setState using spread operator at each level like



                      this.setState(prevState => ({
                      ...prevState,
                      someProperty: {
                      ...prevState.someProperty,
                      someOtherProperty: {
                      ...prevState.someProperty.someOtherProperty,
                      anotherProperty: {
                      ...prevState.someProperty.someOtherProperty.anotherProperty,
                      flag: false
                      }
                      }
                      }
                      }))


                      However the above syntax get every ugly as the state becomes more and more nested and hence I recommend you to use immutability-helper package to update the state.



                      See this answer on how to update state with immutability helper.






                      share|improve this answer














                      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.







                      share|improve this answer














                      share|improve this answer



                      share|improve this answer








                      edited Apr 10 at 18:55









                      Bastiaan ten Klooster

                      13812




                      13812










                      answered Mar 27 '17 at 8:25









                      Shubham Khatri

                      75.1k1384123




                      75.1k1384123








                      • 1




                        @Ohgodwhy , no this is not accessing the state directly since {...this.state.someProperty} retuns a new obejct to someProperty and we are modifying that
                        – Shubham Khatri
                        Apr 15 '17 at 9:17






                      • 2




                        this didn't work for me.. i'm using react version @15.6.1
                        – Rajiv
                        Jun 30 '17 at 20:36






                      • 2




                        @Stophface We can use Lodash to deep clone sure, but not everyone would include this library just to setState
                        – Shubham Khatri
                        Nov 23 '17 at 5:36






                      • 3




                        Doesn't this violate reactjs.org/docs/… ? If two changes to the nested object got batched the last change would overwrite the first. So the most correct way would be: this.setState((prevState) => ({nested: {...prevState.nested, propertyToSet: newValue}}) A bit tedious though...
                        – olejorgenb
                        Dec 21 '17 at 20:55






                      • 1




                        I don't think you need the ...prevState, in the last example, you only someProperty and its children
                        – Jemar Jones
                        May 25 at 19:43














                      • 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






                      • 2




                        this didn't work for me.. i'm using react version @15.6.1
                        – Rajiv
                        Jun 30 '17 at 20:36






                      • 2




                        @Stophface We can use Lodash to deep clone sure, but not everyone would include this library just to setState
                        – Shubham Khatri
                        Nov 23 '17 at 5:36






                      • 3




                        Doesn't this violate reactjs.org/docs/… ? If two changes to the nested object got batched the last change would overwrite the first. So the most correct way would be: this.setState((prevState) => ({nested: {...prevState.nested, propertyToSet: newValue}}) A bit tedious though...
                        – olejorgenb
                        Dec 21 '17 at 20:55






                      • 1




                        I don't think you need the ...prevState, in the last example, you only someProperty and its children
                        – Jemar Jones
                        May 25 at 19:43








                      1




                      1




                      @Ohgodwhy , no this is not accessing the state directly since {...this.state.someProperty} retuns a new obejct to someProperty and we are modifying that
                      – Shubham Khatri
                      Apr 15 '17 at 9:17




                      @Ohgodwhy , no this is not accessing the state directly since {...this.state.someProperty} retuns a new obejct to someProperty and we are modifying that
                      – Shubham Khatri
                      Apr 15 '17 at 9:17




                      2




                      2




                      this didn't work for me.. i'm using react version @15.6.1
                      – Rajiv
                      Jun 30 '17 at 20:36




                      this didn't work for me.. i'm using react version @15.6.1
                      – Rajiv
                      Jun 30 '17 at 20:36




                      2




                      2




                      @Stophface We can use Lodash to deep clone sure, but not everyone would include this library just to setState
                      – Shubham Khatri
                      Nov 23 '17 at 5:36




                      @Stophface We can use Lodash to deep clone sure, but not everyone would include this library just to setState
                      – Shubham Khatri
                      Nov 23 '17 at 5:36




                      3




                      3




                      Doesn't this violate reactjs.org/docs/… ? If two changes to the nested object got batched the last change would overwrite the first. So the most correct way would be: this.setState((prevState) => ({nested: {...prevState.nested, propertyToSet: newValue}}) A bit tedious though...
                      – olejorgenb
                      Dec 21 '17 at 20:55




                      Doesn't this violate reactjs.org/docs/… ? If two changes to the nested object got batched the last change would overwrite the first. So the most correct way would be: this.setState((prevState) => ({nested: {...prevState.nested, propertyToSet: newValue}}) A bit tedious though...
                      – olejorgenb
                      Dec 21 '17 at 20:55




                      1




                      1




                      I don't think you need the ...prevState, in the last example, you only someProperty and its children
                      – Jemar Jones
                      May 25 at 19:43




                      I don't think you need the ...prevState, in the last example, you only someProperty and its children
                      – Jemar Jones
                      May 25 at 19:43












                      up vote
                      57
                      down vote













                      To write it in one line



                      this.setState({ someProperty: { ...this.state.someProperty, flag: false} });





                      share|improve this answer

















                      • 3




                        It is recommended to use the updater for setState instead of directly accessing this.state within setState. reactjs.org/docs/react-component.html#setstate
                        – Raghu Teja
                        May 23 at 12:52










                      • I believe that's saying, don't read this.state immediately after setState and expect it to have the new value. Versus calling this.state within setState. Or is there also an issue with the latter that isn't clear to me?
                        – trpt4him
                        Jun 1 at 13:06










                      • As trpt4him said, the link you gave talks about the problem of accessing this.state after setState. Besides, we are not passing this.state to setState. The operator spread properties. It is the same as Object.assign(). github.com/tc39/proposal-object-rest-spread
                        – Yoseph
                        Jun 4 at 4:27

















                      up vote
                      57
                      down vote













                      To write it in one line



                      this.setState({ someProperty: { ...this.state.someProperty, flag: false} });





                      share|improve this answer

















                      • 3




                        It is recommended to use the updater for setState instead of directly accessing this.state within setState. reactjs.org/docs/react-component.html#setstate
                        – Raghu Teja
                        May 23 at 12:52










                      • I believe that's saying, don't read this.state immediately after setState and expect it to have the new value. Versus calling this.state within setState. Or is there also an issue with the latter that isn't clear to me?
                        – trpt4him
                        Jun 1 at 13:06










                      • As trpt4him said, the link you gave talks about the problem of accessing this.state after setState. Besides, we are not passing this.state to setState. The operator spread properties. It is the same as Object.assign(). github.com/tc39/proposal-object-rest-spread
                        – Yoseph
                        Jun 4 at 4:27















                      up vote
                      57
                      down vote










                      up vote
                      57
                      down vote









                      To write it in one line



                      this.setState({ someProperty: { ...this.state.someProperty, flag: false} });





                      share|improve this answer












                      To write it in one line



                      this.setState({ someProperty: { ...this.state.someProperty, flag: false} });






                      share|improve this answer












                      share|improve this answer



                      share|improve this answer










                      answered Aug 9 '17 at 22:07









                      Yoseph

                      948813




                      948813








                      • 3




                        It is recommended to use the updater for setState instead of directly accessing this.state within setState. reactjs.org/docs/react-component.html#setstate
                        – Raghu Teja
                        May 23 at 12:52










                      • I believe that's saying, don't read this.state immediately after setState and expect it to have the new value. Versus calling this.state within setState. Or is there also an issue with the latter that isn't clear to me?
                        – trpt4him
                        Jun 1 at 13:06










                      • As trpt4him said, the link you gave talks about the problem of accessing this.state after setState. Besides, we are not passing this.state to setState. The operator spread properties. It is the same as Object.assign(). github.com/tc39/proposal-object-rest-spread
                        – Yoseph
                        Jun 4 at 4:27
















                      • 3




                        It is recommended to use the updater for setState instead of directly accessing this.state within setState. reactjs.org/docs/react-component.html#setstate
                        – Raghu Teja
                        May 23 at 12:52










                      • I believe that's saying, don't read this.state immediately after setState and expect it to have the new value. Versus calling this.state within setState. Or is there also an issue with the latter that isn't clear to me?
                        – trpt4him
                        Jun 1 at 13:06










                      • As trpt4him said, the link you gave talks about the problem of accessing this.state after setState. Besides, we are not passing this.state to setState. The operator spread properties. It is the same as Object.assign(). github.com/tc39/proposal-object-rest-spread
                        – Yoseph
                        Jun 4 at 4:27










                      3




                      3




                      It is recommended to use the updater for setState instead of directly accessing this.state within setState. reactjs.org/docs/react-component.html#setstate
                      – Raghu Teja
                      May 23 at 12:52




                      It is recommended to use the updater for setState instead of directly accessing this.state within setState. reactjs.org/docs/react-component.html#setstate
                      – Raghu Teja
                      May 23 at 12:52












                      I believe that's saying, don't read this.state immediately after setState and expect it to have the new value. Versus calling this.state within setState. Or is there also an issue with the latter that isn't clear to me?
                      – trpt4him
                      Jun 1 at 13:06




                      I believe that's saying, don't read this.state immediately after setState and expect it to have the new value. Versus calling this.state within setState. Or is there also an issue with the latter that isn't clear to me?
                      – trpt4him
                      Jun 1 at 13:06












                      As trpt4him said, the link you gave talks about the problem of accessing this.state after setState. Besides, we are not passing this.state to setState. The operator spread properties. It is the same as Object.assign(). github.com/tc39/proposal-object-rest-spread
                      – Yoseph
                      Jun 4 at 4:27






                      As trpt4him said, the link you gave talks about the problem of accessing this.state after setState. Besides, we are not passing this.state to setState. The operator spread properties. It is the same as Object.assign(). github.com/tc39/proposal-object-rest-spread
                      – Yoseph
                      Jun 4 at 4:27












                      up vote
                      23
                      down vote













                      Sometimes direct answers are not the best ones :)



                      Short version:



                      this code



                      this.state = {
                      someProperty: {
                      flag: true
                      }
                      }


                      should be simplified as something like



                      this.state = {
                      somePropertyFlag: true
                      }


                      Long version:



                      Currently you shouldn't want to work with nested state in React. Because React is not oriented to work with nested states and all solutions proposed here look as hacks. They don't use the framework but fight with it. They suggest to write not so clear code for doubtful purpose of grouping some properties. So they are very interesting as an answer to the challenge but practically useless.



                      Lets imagine the following state:



                      {
                      parent: {
                      child1: 'value 1',
                      child2: 'value 2',
                      ...
                      child100: 'value 100'
                      }
                      }


                      What will happen if you change just a value of child1? React will not re-render the view because it uses shallow comparison and it will find that parent property didn't change. BTW mutating the state object directly is considered to be a bad practice in general.



                      So you need to re-create the whole parent object. But in this case we will meet another problem. React will think that all children have changed their values and will re-render all of them. Of course it is not good for performance.



                      It is still possible to solve that problem by writing some complicated logic in shouldComponentUpdate() but I would prefer to stop here and use simple solution from the short version.






                      share|improve this answer





















                      • I think there are use cases where a nested state is perfectly fine. E.g. a state dynamically keeping track of key-value pairs. See here how you can update the state for just a single entry in the state: reactjs.org/docs/…
                        – pors
                        Aug 30 at 13:45










                      • Shallow comparison is used for pure components by default.
                        – Basel Shishani
                        Sep 26 at 12:10















                      up vote
                      23
                      down vote













                      Sometimes direct answers are not the best ones :)



                      Short version:



                      this code



                      this.state = {
                      someProperty: {
                      flag: true
                      }
                      }


                      should be simplified as something like



                      this.state = {
                      somePropertyFlag: true
                      }


                      Long version:



                      Currently you shouldn't want to work with nested state in React. Because React is not oriented to work with nested states and all solutions proposed here look as hacks. They don't use the framework but fight with it. They suggest to write not so clear code for doubtful purpose of grouping some properties. So they are very interesting as an answer to the challenge but practically useless.



                      Lets imagine the following state:



                      {
                      parent: {
                      child1: 'value 1',
                      child2: 'value 2',
                      ...
                      child100: 'value 100'
                      }
                      }


                      What will happen if you change just a value of child1? React will not re-render the view because it uses shallow comparison and it will find that parent property didn't change. BTW mutating the state object directly is considered to be a bad practice in general.



                      So you need to re-create the whole parent object. But in this case we will meet another problem. React will think that all children have changed their values and will re-render all of them. Of course it is not good for performance.



                      It is still possible to solve that problem by writing some complicated logic in shouldComponentUpdate() but I would prefer to stop here and use simple solution from the short version.






                      share|improve this answer





















                      • I think there are use cases where a nested state is perfectly fine. E.g. a state dynamically keeping track of key-value pairs. See here how you can update the state for just a single entry in the state: reactjs.org/docs/…
                        – pors
                        Aug 30 at 13:45










                      • Shallow comparison is used for pure components by default.
                        – Basel Shishani
                        Sep 26 at 12:10













                      up vote
                      23
                      down vote










                      up vote
                      23
                      down vote









                      Sometimes direct answers are not the best ones :)



                      Short version:



                      this code



                      this.state = {
                      someProperty: {
                      flag: true
                      }
                      }


                      should be simplified as something like



                      this.state = {
                      somePropertyFlag: true
                      }


                      Long version:



                      Currently you shouldn't want to work with nested state in React. Because React is not oriented to work with nested states and all solutions proposed here look as hacks. They don't use the framework but fight with it. They suggest to write not so clear code for doubtful purpose of grouping some properties. So they are very interesting as an answer to the challenge but practically useless.



                      Lets imagine the following state:



                      {
                      parent: {
                      child1: 'value 1',
                      child2: 'value 2',
                      ...
                      child100: 'value 100'
                      }
                      }


                      What will happen if you change just a value of child1? React will not re-render the view because it uses shallow comparison and it will find that parent property didn't change. BTW mutating the state object directly is considered to be a bad practice in general.



                      So you need to re-create the whole parent object. But in this case we will meet another problem. React will think that all children have changed their values and will re-render all of them. Of course it is not good for performance.



                      It is still possible to solve that problem by writing some complicated logic in shouldComponentUpdate() but I would prefer to stop here and use simple solution from the short version.






                      share|improve this answer












                      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.







                      share|improve this answer












                      share|improve this answer



                      share|improve this answer










                      answered Jul 2 at 12:18









                      Konstantin Smolyanin

                      11.1k53932




                      11.1k53932












                      • I think there are use cases where a nested state is perfectly fine. E.g. a state dynamically keeping track of key-value pairs. See here how you can update the state for just a single entry in the state: reactjs.org/docs/…
                        – pors
                        Aug 30 at 13:45










                      • Shallow comparison is used for pure components by default.
                        – Basel Shishani
                        Sep 26 at 12:10


















                      • I think there are use cases where a nested state is perfectly fine. E.g. a state dynamically keeping track of key-value pairs. See here how you can update the state for just a single entry in the state: reactjs.org/docs/…
                        – pors
                        Aug 30 at 13:45










                      • Shallow comparison is used for pure components by default.
                        – Basel Shishani
                        Sep 26 at 12:10
















                      I think there are use cases where a nested state is perfectly fine. E.g. a state dynamically keeping track of key-value pairs. See here how you can update the state for just a single entry in the state: reactjs.org/docs/…
                      – pors
                      Aug 30 at 13:45




                      I think there are use cases where a nested state is perfectly fine. E.g. a state dynamically keeping track of key-value pairs. See here how you can update the state for just a single entry in the state: reactjs.org/docs/…
                      – pors
                      Aug 30 at 13:45












                      Shallow comparison is used for pure components by default.
                      – Basel Shishani
                      Sep 26 at 12:10




                      Shallow comparison is used for pure components by default.
                      – Basel Shishani
                      Sep 26 at 12:10










                      up vote
                      18
                      down vote













                      If you are using ES2015 you have access to the Object.assign. You can use it as follows to update a nested object.



                      this.setState({
                      someProperty: Object.assign({}, this.state.someProperty, {flag: false})
                      });


                      You merge the updated properties with the existing and use the returned object to update the state.



                      Edit: Added an empty object as target to the assign function to make sure the state isn't mutated directly as carkod pointed out.






                      share|improve this answer



















                      • 8




                        And I could be wrong but I don't think you're using React without ES2015.
                        – Madbreaks
                        Nov 10 '17 at 18:29

















                      up vote
                      18
                      down vote













                      If you are using ES2015 you have access to the Object.assign. You can use it as follows to update a nested object.



                      this.setState({
                      someProperty: Object.assign({}, this.state.someProperty, {flag: false})
                      });


                      You merge the updated properties with the existing and use the returned object to update the state.



                      Edit: Added an empty object as target to the assign function to make sure the state isn't mutated directly as carkod pointed out.






                      share|improve this answer



















                      • 8




                        And I could be wrong but I don't think you're using React without ES2015.
                        – Madbreaks
                        Nov 10 '17 at 18:29















                      up vote
                      18
                      down vote










                      up vote
                      18
                      down vote









                      If you are using ES2015 you have access to the Object.assign. You can use it as follows to update a nested object.



                      this.setState({
                      someProperty: Object.assign({}, this.state.someProperty, {flag: false})
                      });


                      You merge the updated properties with the existing and use the returned object to update the state.



                      Edit: Added an empty object as target to the assign function to make sure the state isn't mutated directly as carkod pointed out.






                      share|improve this answer














                      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.







                      share|improve this answer














                      share|improve this answer



                      share|improve this answer








                      edited Feb 7 at 6:58

























                      answered Oct 7 '17 at 19:20









                      Alyssa Roose

                      18915




                      18915








                      • 8




                        And I could be wrong but I don't think you're using React without ES2015.
                        – Madbreaks
                        Nov 10 '17 at 18:29
















                      • 8




                        And I could be wrong but I don't think you're using React without ES2015.
                        – Madbreaks
                        Nov 10 '17 at 18:29










                      8




                      8




                      And I could be wrong but I don't think you're using React without ES2015.
                      – Madbreaks
                      Nov 10 '17 at 18:29






                      And I could be wrong but I don't think you're using React without ES2015.
                      – Madbreaks
                      Nov 10 '17 at 18:29












                      up vote
                      12
                      down vote













                      Disclaimer




                      Nested State in React is wrong design



                      Read this excellent answer.




                       




                      Reasoning behind this answer:



                      React's setState is just a built-in convenience, but you soon realise
                      that it has its limits. Using custom properties and intelligent use of
                      forceUpdate gives you much more.
                      eg:



                      class MyClass extends React.Component {
                      myState = someObject
                      inputValue = 42
                      ...


                      MobX, for example, ditches state completely and uses custom observable properties.
                      Use Observables instead of state in React components.




                       





                      the best answer of all - see example here



                      There is another shorter way to update whatever nested property.



                      this.setState(state => {
                      state.nested.flag = false
                      state.another.deep.prop = true
                      return state
                      })


                      On one line




                      this.setState(state => (state.nested.flag = false, state))



                      Of course this is abusing some core principles, as the state should be read-only, but since you are immediately discarding the old state and replacing it with new state, it is completely ok.



                      Warning



                      Even though the component containing the state will update and rerender properly (except this gotcha), the props will fail to propagate to children (see Spymaster's comment below). Only use this technique if you know what you are doing.



                      For example, you may pass a changed flat prop that is updated and passed easily.



                      render(
                      //some complex render with your nested state
                      <ChildComponent complexNestedProp={this.state.nested} pleaseRerender={Math.random()}/>
                      )


                      Now even though reference for complexNestedProp did not change (shouldComponentUpdate)



                      this.props.complexNestedProp === nextProps.complexNestedProp


                      the component will rerender whenever parent component updates, which is the case after calling this.setState or this.forceUpdate in the parent.






                      share|improve this answer



















                      • 6




                        You shouldn't be mutating any react state anywhere. If there are nested components that use data from state.nested or state.another, they won't re-render nor will their children. This is because the object did not change, thus React thinks nothing has changed.
                        – SpyMaster356
                        May 12 at 16:51










                      • @SpyMaster356 I have updated my answer to address this. Also note that MobX, for example, ditches state completely and uses custom observable properties. React's setState is just a built-in convenience, but you soon realise that it has its limits. Using custom properties and intelligent use of forceUpdate gives you much more. Use Observables instead of state in React components.
                        – Qwerty
                        Aug 9 at 9:44












                      • Also I added an example react-experiments.herokuapp.com/state-flow (link to git at the top)
                        – Qwerty
                        Aug 9 at 16:28















                      up vote
                      12
                      down vote













                      Disclaimer




                      Nested State in React is wrong design



                      Read this excellent answer.




                       




                      Reasoning behind this answer:



                      React's setState is just a built-in convenience, but you soon realise
                      that it has its limits. Using custom properties and intelligent use of
                      forceUpdate gives you much more.
                      eg:



                      class MyClass extends React.Component {
                      myState = someObject
                      inputValue = 42
                      ...


                      MobX, for example, ditches state completely and uses custom observable properties.
                      Use Observables instead of state in React components.




                       





                      the best answer of all - see example here



                      There is another shorter way to update whatever nested property.



                      this.setState(state => {
                      state.nested.flag = false
                      state.another.deep.prop = true
                      return state
                      })


                      On one line




                      this.setState(state => (state.nested.flag = false, state))



                      Of course this is abusing some core principles, as the state should be read-only, but since you are immediately discarding the old state and replacing it with new state, it is completely ok.



                      Warning



                      Even though the component containing the state will update and rerender properly (except this gotcha), the props will fail to propagate to children (see Spymaster's comment below). Only use this technique if you know what you are doing.



                      For example, you may pass a changed flat prop that is updated and passed easily.



                      render(
                      //some complex render with your nested state
                      <ChildComponent complexNestedProp={this.state.nested} pleaseRerender={Math.random()}/>
                      )


                      Now even though reference for complexNestedProp did not change (shouldComponentUpdate)



                      this.props.complexNestedProp === nextProps.complexNestedProp


                      the component will rerender whenever parent component updates, which is the case after calling this.setState or this.forceUpdate in the parent.






                      share|improve this answer



















                      • 6




                        You shouldn't be mutating any react state anywhere. If there are nested components that use data from state.nested or state.another, they won't re-render nor will their children. This is because the object did not change, thus React thinks nothing has changed.
                        – SpyMaster356
                        May 12 at 16:51










                      • @SpyMaster356 I have updated my answer to address this. Also note that MobX, for example, ditches state completely and uses custom observable properties. React's setState is just a built-in convenience, but you soon realise that it has its limits. Using custom properties and intelligent use of forceUpdate gives you much more. Use Observables instead of state in React components.
                        – Qwerty
                        Aug 9 at 9:44












                      • Also I added an example react-experiments.herokuapp.com/state-flow (link to git at the top)
                        – Qwerty
                        Aug 9 at 16:28













                      up vote
                      12
                      down vote










                      up vote
                      12
                      down vote









                      Disclaimer




                      Nested State in React is wrong design



                      Read this excellent answer.




                       




                      Reasoning behind this answer:



                      React's setState is just a built-in convenience, but you soon realise
                      that it has its limits. Using custom properties and intelligent use of
                      forceUpdate gives you much more.
                      eg:



                      class MyClass extends React.Component {
                      myState = someObject
                      inputValue = 42
                      ...


                      MobX, for example, ditches state completely and uses custom observable properties.
                      Use Observables instead of state in React components.




                       





                      the best answer of all - see example here



                      There is another shorter way to update whatever nested property.



                      this.setState(state => {
                      state.nested.flag = false
                      state.another.deep.prop = true
                      return state
                      })


                      On one line




                      this.setState(state => (state.nested.flag = false, state))



                      Of course this is abusing some core principles, as the state should be read-only, but since you are immediately discarding the old state and replacing it with new state, it is completely ok.



                      Warning



                      Even though the component containing the state will update and rerender properly (except this gotcha), the props will fail to propagate to children (see Spymaster's comment below). Only use this technique if you know what you are doing.



                      For example, you may pass a changed flat prop that is updated and passed easily.



                      render(
                      //some complex render with your nested state
                      <ChildComponent complexNestedProp={this.state.nested} pleaseRerender={Math.random()}/>
                      )


                      Now even though reference for complexNestedProp did not change (shouldComponentUpdate)



                      this.props.complexNestedProp === nextProps.complexNestedProp


                      the component will rerender whenever parent component updates, which is the case after calling this.setState or this.forceUpdate in the parent.






                      share|improve this answer














                      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.







                      share|improve this answer














                      share|improve this answer



                      share|improve this answer








                      edited Oct 18 at 10:04

























                      answered May 3 at 7:50









                      Qwerty

                      8,67375067




                      8,67375067








                      • 6




                        You shouldn't be mutating any react state anywhere. If there are nested components that use data from state.nested or state.another, they won't re-render nor will their children. This is because the object did not change, thus React thinks nothing has changed.
                        – SpyMaster356
                        May 12 at 16:51










                      • @SpyMaster356 I have updated my answer to address this. Also note that MobX, for example, ditches state completely and uses custom observable properties. React's setState is just a built-in convenience, but you soon realise that it has its limits. Using custom properties and intelligent use of forceUpdate gives you much more. Use Observables instead of state in React components.
                        – Qwerty
                        Aug 9 at 9:44












                      • Also I added an example react-experiments.herokuapp.com/state-flow (link to git at the top)
                        – Qwerty
                        Aug 9 at 16:28














                      • 6




                        You shouldn't be mutating any react state anywhere. If there are nested components that use data from state.nested or state.another, they won't re-render nor will their children. This is because the object did not change, thus React thinks nothing has changed.
                        – SpyMaster356
                        May 12 at 16:51










                      • @SpyMaster356 I have updated my answer to address this. Also note that MobX, for example, ditches state completely and uses custom observable properties. React's setState is just a built-in convenience, but you soon realise that it has its limits. Using custom properties and intelligent use of forceUpdate gives you much more. Use Observables instead of state in React components.
                        – Qwerty
                        Aug 9 at 9:44












                      • Also I added an example react-experiments.herokuapp.com/state-flow (link to git at the top)
                        – Qwerty
                        Aug 9 at 16:28








                      6




                      6




                      You shouldn't be mutating any react state anywhere. If there are nested components that use data from state.nested or state.another, they won't re-render nor will their children. This is because the object did not change, thus React thinks nothing has changed.
                      – SpyMaster356
                      May 12 at 16:51




                      You shouldn't be mutating any react state anywhere. If there are nested components that use data from state.nested or state.another, they won't re-render nor will their children. This is because the object did not change, thus React thinks nothing has changed.
                      – SpyMaster356
                      May 12 at 16:51












                      @SpyMaster356 I have updated my answer to address this. Also note that MobX, for example, ditches state completely and uses custom observable properties. React's setState is just a built-in convenience, but you soon realise that it has its limits. Using custom properties and intelligent use of forceUpdate gives you much more. Use Observables instead of state in React components.
                      – Qwerty
                      Aug 9 at 9:44






                      @SpyMaster356 I have updated my answer to address this. Also note that MobX, for example, ditches state completely and uses custom observable properties. React's setState is just a built-in convenience, but you soon realise that it has its limits. Using custom properties and intelligent use of forceUpdate gives you much more. Use Observables instead of state in React components.
                      – Qwerty
                      Aug 9 at 9:44














                      Also I added an example react-experiments.herokuapp.com/state-flow (link to git at the top)
                      – Qwerty
                      Aug 9 at 16:28




                      Also I added an example react-experiments.herokuapp.com/state-flow (link to git at the top)
                      – Qwerty
                      Aug 9 at 16:28










                      up vote
                      6
                      down vote













                      There are many libraries to help with this. For example, using immutability-helper:



                      import update from 'immutability-helper';

                      const newState = update(this.state, {
                      someProperty: {flag: {$set: false}},
                      };
                      this.setState(newState);


                      Or using lodash/fp:



                      import {merge} from 'lodash/fp';

                      const newState = merge(this.state, {
                      someProperty: {flag: false},
                      });





                      share|improve this answer























                      • If you use lodash, you probably want to try _.cloneDeep and set state to be the cloned copy.
                        – Yixing Liu
                        Jul 26 at 20:37










                      • @YixingLiu: I've updated the answer to use lodash/fp, so we don't have to worry about mutations.
                        – tokland
                        Jul 26 at 20:50

















                      up vote
                      6
                      down vote













                      There are many libraries to help with this. For example, using immutability-helper:



                      import update from 'immutability-helper';

                      const newState = update(this.state, {
                      someProperty: {flag: {$set: false}},
                      };
                      this.setState(newState);


                      Or using lodash/fp:



                      import {merge} from 'lodash/fp';

                      const newState = merge(this.state, {
                      someProperty: {flag: false},
                      });





                      share|improve this answer























                      • If you use lodash, you probably want to try _.cloneDeep and set state to be the cloned copy.
                        – Yixing Liu
                        Jul 26 at 20:37










                      • @YixingLiu: I've updated the answer to use lodash/fp, so we don't have to worry about mutations.
                        – tokland
                        Jul 26 at 20:50















                      up vote
                      6
                      down vote










                      up vote
                      6
                      down vote









                      There are many libraries to help with this. For example, using immutability-helper:



                      import update from 'immutability-helper';

                      const newState = update(this.state, {
                      someProperty: {flag: {$set: false}},
                      };
                      this.setState(newState);


                      Or using lodash/fp:



                      import {merge} from 'lodash/fp';

                      const newState = merge(this.state, {
                      someProperty: {flag: false},
                      });





                      share|improve this answer














                      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},
                      });






                      share|improve this answer














                      share|improve this answer



                      share|improve this answer








                      edited Aug 13 at 18:42

























                      answered May 19 '17 at 8:15









                      tokland

                      51.2k11114142




                      51.2k11114142












                      • If you use lodash, you probably want to try _.cloneDeep and set state to be the cloned copy.
                        – Yixing Liu
                        Jul 26 at 20:37










                      • @YixingLiu: I've updated the answer to use lodash/fp, so we don't have to worry about mutations.
                        – tokland
                        Jul 26 at 20:50




















                      • If you use lodash, you probably want to try _.cloneDeep and set state to be the cloned copy.
                        – Yixing Liu
                        Jul 26 at 20:37










                      • @YixingLiu: I've updated the answer to use lodash/fp, so we don't have to worry about mutations.
                        – tokland
                        Jul 26 at 20:50


















                      If you use lodash, you probably want to try _.cloneDeep and set state to be the cloned copy.
                      – Yixing Liu
                      Jul 26 at 20:37




                      If you use lodash, you probably want to try _.cloneDeep and set state to be the cloned copy.
                      – Yixing Liu
                      Jul 26 at 20:37












                      @YixingLiu: I've updated the answer to use lodash/fp, so we don't have to worry about mutations.
                      – tokland
                      Jul 26 at 20:50






                      @YixingLiu: I've updated the answer to use lodash/fp, so we don't have to worry about mutations.
                      – tokland
                      Jul 26 at 20:50












                      up vote
                      5
                      down vote













                      Here's a variation on the first answer given in this thread which doesn't require any extra packages, libraries or special functions.



                      state = {
                      someProperty: {
                      flag: 'string'
                      }
                      }

                      handleChange = (value) => {
                      const newState = {...this.state.someProperty, flag: value}
                      this.setState({ someProperty: newState })
                      }


                      In order to set the state of a specific nested field, you have set the whole object. I did this by creating a variable, newState and spreading the contents of the current state into it first using the ES2015 spread operator. Then, I replaced the value of this.state.flag with the new value (since I set flag: value after I spread the current state into the object, the flag field in the current state is overridden). Then, I simply set the state of someProperty to my newState object.






                      share|improve this answer

























                        up vote
                        5
                        down vote













                        Here's a variation on the first answer given in this thread which doesn't require any extra packages, libraries or special functions.



                        state = {
                        someProperty: {
                        flag: 'string'
                        }
                        }

                        handleChange = (value) => {
                        const newState = {...this.state.someProperty, flag: value}
                        this.setState({ someProperty: newState })
                        }


                        In order to set the state of a specific nested field, you have set the whole object. I did this by creating a variable, newState and spreading the contents of the current state into it first using the ES2015 spread operator. Then, I replaced the value of this.state.flag with the new value (since I set flag: value after I spread the current state into the object, the flag field in the current state is overridden). Then, I simply set the state of someProperty to my newState object.






                        share|improve this answer























                          up vote
                          5
                          down vote










                          up vote
                          5
                          down vote









                          Here's a variation on the first answer given in this thread which doesn't require any extra packages, libraries or special functions.



                          state = {
                          someProperty: {
                          flag: 'string'
                          }
                          }

                          handleChange = (value) => {
                          const newState = {...this.state.someProperty, flag: value}
                          this.setState({ someProperty: newState })
                          }


                          In order to set the state of a specific nested field, you have set the whole object. I did this by creating a variable, newState and spreading the contents of the current state into it first using the ES2015 spread operator. Then, I replaced the value of this.state.flag with the new value (since I set flag: value after I spread the current state into the object, the flag field in the current state is overridden). Then, I simply set the state of someProperty to my newState object.






                          share|improve this answer












                          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.







                          share|improve this answer












                          share|improve this answer



                          share|improve this answer










                          answered Nov 29 '17 at 17:10









                          Matthew Berkompas

                          6112




                          6112






















                              up vote
                              5
                              down vote













                              You can also go this way (which feels more readable to me):



                              const newState = Object.assign({}, this.state);
                              newState.property.nestedProperty = 'newValue';
                              this.setState(newState);





                              share|improve this answer

























                                up vote
                                5
                                down vote













                                You can also go this way (which feels more readable to me):



                                const newState = Object.assign({}, this.state);
                                newState.property.nestedProperty = 'newValue';
                                this.setState(newState);





                                share|improve this answer























                                  up vote
                                  5
                                  down vote










                                  up vote
                                  5
                                  down vote









                                  You can also go this way (which feels more readable to me):



                                  const newState = Object.assign({}, this.state);
                                  newState.property.nestedProperty = 'newValue';
                                  this.setState(newState);





                                  share|improve this answer












                                  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);






                                  share|improve this answer












                                  share|improve this answer



                                  share|improve this answer










                                  answered Apr 7 at 13:30









                                  enzolito

                                  7817




                                  7817






















                                      up vote
                                      2
                                      down vote













                                      I used this solution.



                                      If you have a nested state like this:



                                         this.state = {
                                      formInputs:{
                                      friendName:{
                                      value:'',
                                      isValid:false,
                                      errorMsg:''
                                      },
                                      friendEmail:{
                                      value:'',
                                      isValid:false,
                                      errorMsg:''
                                      }
                                      }


                                      you can declare the handleChange function that copy current status and re-assigns it with changed values



                                      handleChange(el) {
                                      let inputName = el.target.name;
                                      let inputValue = el.target.value;

                                      let statusCopy = Object.assign({}, this.state);
                                      statusCopy.formInputs[inputName].value = inputValue;

                                      this.setState(statusCopy);
                                      }


                                      here the html with the event listener



                                      <input type="text" onChange={this.handleChange} " name="friendName" />





                                      share|improve this answer

























                                        up vote
                                        2
                                        down vote













                                        I used this solution.



                                        If you have a nested state like this:



                                           this.state = {
                                        formInputs:{
                                        friendName:{
                                        value:'',
                                        isValid:false,
                                        errorMsg:''
                                        },
                                        friendEmail:{
                                        value:'',
                                        isValid:false,
                                        errorMsg:''
                                        }
                                        }


                                        you can declare the handleChange function that copy current status and re-assigns it with changed values



                                        handleChange(el) {
                                        let inputName = el.target.name;
                                        let inputValue = el.target.value;

                                        let statusCopy = Object.assign({}, this.state);
                                        statusCopy.formInputs[inputName].value = inputValue;

                                        this.setState(statusCopy);
                                        }


                                        here the html with the event listener



                                        <input type="text" onChange={this.handleChange} " name="friendName" />





                                        share|improve this answer























                                          up vote
                                          2
                                          down vote










                                          up vote
                                          2
                                          down vote









                                          I used this solution.



                                          If you have a nested state like this:



                                             this.state = {
                                          formInputs:{
                                          friendName:{
                                          value:'',
                                          isValid:false,
                                          errorMsg:''
                                          },
                                          friendEmail:{
                                          value:'',
                                          isValid:false,
                                          errorMsg:''
                                          }
                                          }


                                          you can declare the handleChange function that copy current status and re-assigns it with changed values



                                          handleChange(el) {
                                          let inputName = el.target.name;
                                          let inputValue = el.target.value;

                                          let statusCopy = Object.assign({}, this.state);
                                          statusCopy.formInputs[inputName].value = inputValue;

                                          this.setState(statusCopy);
                                          }


                                          here the html with the event listener



                                          <input type="text" onChange={this.handleChange} " name="friendName" />





                                          share|improve this answer












                                          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" />






                                          share|improve this answer












                                          share|improve this answer



                                          share|improve this answer










                                          answered Mar 6 at 9:40









                                          Alberto Piras

                                          813




                                          813






















                                              up vote
                                              2
                                              down vote













                                              Two other options not mentioned yet:




                                              1. 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.

                                              2. 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.






                                              share|improve this answer



























                                                up vote
                                                2
                                                down vote













                                                Two other options not mentioned yet:




                                                1. 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.

                                                2. 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.






                                                share|improve this answer

























                                                  up vote
                                                  2
                                                  down vote










                                                  up vote
                                                  2
                                                  down vote









                                                  Two other options not mentioned yet:




                                                  1. 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.

                                                  2. 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.






                                                  share|improve this answer














                                                  Two other options not mentioned yet:




                                                  1. 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.

                                                  2. 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.







                                                  share|improve this answer














                                                  share|improve this answer



                                                  share|improve this answer








                                                  edited Jun 28 at 2:57

























                                                  answered Jun 28 at 2:51









                                                  Cory House

                                                  7,82775674




                                                  7,82775674






















                                                      up vote
                                                      1
                                                      down vote













                                                      To make things generic, I worked on @ShubhamKhatri's and @Qwerty's answers.



                                                      state object



                                                      this.state = {
                                                      name: '',
                                                      grandParent: {
                                                      parent1: {
                                                      child: ''
                                                      },
                                                      parent2: {
                                                      child: ''
                                                      }
                                                      }
                                                      };


                                                      input controls



                                                      <input
                                                      value={this.state.name}
                                                      onChange={this.updateState}
                                                      type="text"
                                                      name="name"
                                                      />
                                                      <input
                                                      value={this.state.grandParent.parent1.child}
                                                      onChange={this.updateState}
                                                      type="text"
                                                      name="grandParent.parent1.child"
                                                      />
                                                      <input
                                                      value={this.state.grandParent.parent2.child}
                                                      onChange={this.updateState}
                                                      type="text"
                                                      name="grandParent.parent2.child"
                                                      />


                                                      updateState method



                                                      setState as @ShubhamKhatri's answer



                                                      updateState(event) {
                                                      const path = event.target.name.split('.');
                                                      const depth = path.length;
                                                      const oldstate = this.state;
                                                      const newstate = { ...oldstate };
                                                      let newStateLevel = newstate;
                                                      let oldStateLevel = oldstate;

                                                      for (let i = 0; i < depth; i += 1) {
                                                      if (i === depth - 1) {
                                                      newStateLevel[path[i]] = event.target.value;
                                                      } else {
                                                      newStateLevel[path[i]] = { ...oldStateLevel[path[i]] };
                                                      oldStateLevel = oldStateLevel[path[i]];
                                                      newStateLevel = newStateLevel[path[i]];
                                                      }
                                                      }
                                                      this.setState(newstate);
                                                      }


                                                      setState as @Qwerty's answer



                                                      updateState(event) {
                                                      const path = event.target.name.split('.');
                                                      const depth = path.length;
                                                      const state = { ...this.state };
                                                      let ref = state;
                                                      for (let i = 0; i < depth; i += 1) {
                                                      if (i === depth - 1) {
                                                      ref[path[i]] = event.target.value;
                                                      } else {
                                                      ref = ref[path[i]];
                                                      }
                                                      }
                                                      this.setState(state);
                                                      }


                                                      Note: These above methods won't work for arrays






                                                      share|improve this answer

























                                                        up vote
                                                        1
                                                        down vote













                                                        To make things generic, I worked on @ShubhamKhatri's and @Qwerty's answers.



                                                        state object



                                                        this.state = {
                                                        name: '',
                                                        grandParent: {
                                                        parent1: {
                                                        child: ''
                                                        },
                                                        parent2: {
                                                        child: ''
                                                        }
                                                        }
                                                        };


                                                        input controls



                                                        <input
                                                        value={this.state.name}
                                                        onChange={this.updateState}
                                                        type="text"
                                                        name="name"
                                                        />
                                                        <input
                                                        value={this.state.grandParent.parent1.child}
                                                        onChange={this.updateState}
                                                        type="text"
                                                        name="grandParent.parent1.child"
                                                        />
                                                        <input
                                                        value={this.state.grandParent.parent2.child}
                                                        onChange={this.updateState}
                                                        type="text"
                                                        name="grandParent.parent2.child"
                                                        />


                                                        updateState method



                                                        setState as @ShubhamKhatri's answer



                                                        updateState(event) {
                                                        const path = event.target.name.split('.');
                                                        const depth = path.length;
                                                        const oldstate = this.state;
                                                        const newstate = { ...oldstate };
                                                        let newStateLevel = newstate;
                                                        let oldStateLevel = oldstate;

                                                        for (let i = 0; i < depth; i += 1) {
                                                        if (i === depth - 1) {
                                                        newStateLevel[path[i]] = event.target.value;
                                                        } else {
                                                        newStateLevel[path[i]] = { ...oldStateLevel[path[i]] };
                                                        oldStateLevel = oldStateLevel[path[i]];
                                                        newStateLevel = newStateLevel[path[i]];
                                                        }
                                                        }
                                                        this.setState(newstate);
                                                        }


                                                        setState as @Qwerty's answer



                                                        updateState(event) {
                                                        const path = event.target.name.split('.');
                                                        const depth = path.length;
                                                        const state = { ...this.state };
                                                        let ref = state;
                                                        for (let i = 0; i < depth; i += 1) {
                                                        if (i === depth - 1) {
                                                        ref[path[i]] = event.target.value;
                                                        } else {
                                                        ref = ref[path[i]];
                                                        }
                                                        }
                                                        this.setState(state);
                                                        }


                                                        Note: These above methods won't work for arrays






                                                        share|improve this answer























                                                          up vote
                                                          1
                                                          down vote










                                                          up vote
                                                          1
                                                          down vote









                                                          To make things generic, I worked on @ShubhamKhatri's and @Qwerty's answers.



                                                          state object



                                                          this.state = {
                                                          name: '',
                                                          grandParent: {
                                                          parent1: {
                                                          child: ''
                                                          },
                                                          parent2: {
                                                          child: ''
                                                          }
                                                          }
                                                          };


                                                          input controls



                                                          <input
                                                          value={this.state.name}
                                                          onChange={this.updateState}
                                                          type="text"
                                                          name="name"
                                                          />
                                                          <input
                                                          value={this.state.grandParent.parent1.child}
                                                          onChange={this.updateState}
                                                          type="text"
                                                          name="grandParent.parent1.child"
                                                          />
                                                          <input
                                                          value={this.state.grandParent.parent2.child}
                                                          onChange={this.updateState}
                                                          type="text"
                                                          name="grandParent.parent2.child"
                                                          />


                                                          updateState method



                                                          setState as @ShubhamKhatri's answer



                                                          updateState(event) {
                                                          const path = event.target.name.split('.');
                                                          const depth = path.length;
                                                          const oldstate = this.state;
                                                          const newstate = { ...oldstate };
                                                          let newStateLevel = newstate;
                                                          let oldStateLevel = oldstate;

                                                          for (let i = 0; i < depth; i += 1) {
                                                          if (i === depth - 1) {
                                                          newStateLevel[path[i]] = event.target.value;
                                                          } else {
                                                          newStateLevel[path[i]] = { ...oldStateLevel[path[i]] };
                                                          oldStateLevel = oldStateLevel[path[i]];
                                                          newStateLevel = newStateLevel[path[i]];
                                                          }
                                                          }
                                                          this.setState(newstate);
                                                          }


                                                          setState as @Qwerty's answer



                                                          updateState(event) {
                                                          const path = event.target.name.split('.');
                                                          const depth = path.length;
                                                          const state = { ...this.state };
                                                          let ref = state;
                                                          for (let i = 0; i < depth; i += 1) {
                                                          if (i === depth - 1) {
                                                          ref[path[i]] = event.target.value;
                                                          } else {
                                                          ref = ref[path[i]];
                                                          }
                                                          }
                                                          this.setState(state);
                                                          }


                                                          Note: These above methods won't work for arrays






                                                          share|improve this answer












                                                          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







                                                          share|improve this answer












                                                          share|improve this answer



                                                          share|improve this answer










                                                          answered Jul 2 at 8:29









                                                          Venugopal

                                                          1,62911227




                                                          1,62911227






















                                                              up vote
                                                              1
                                                              down vote













                                                              We use Immer https://github.com/mweststrate/immer to handle these kinds of issues.



                                                              Just replaced this code in one of our components



                                                              this.setState(prevState => ({
                                                              ...prevState,
                                                              preferences: {
                                                              ...prevState.preferences,
                                                              [key]: newValue
                                                              }
                                                              }));


                                                              With this



                                                              import produce from 'immer';

                                                              this.setState(produce(draft => {
                                                              draft.preferences[key] = newValue;
                                                              }));


                                                              With immer you handle your state as a "normal object".
                                                              The magic happens behind the scene with proxy objects.






                                                              share|improve this answer

























                                                                up vote
                                                                1
                                                                down vote













                                                                We use Immer https://github.com/mweststrate/immer to handle these kinds of issues.



                                                                Just replaced this code in one of our components



                                                                this.setState(prevState => ({
                                                                ...prevState,
                                                                preferences: {
                                                                ...prevState.preferences,
                                                                [key]: newValue
                                                                }
                                                                }));


                                                                With this



                                                                import produce from 'immer';

                                                                this.setState(produce(draft => {
                                                                draft.preferences[key] = newValue;
                                                                }));


                                                                With immer you handle your state as a "normal object".
                                                                The magic happens behind the scene with proxy objects.






                                                                share|improve this answer























                                                                  up vote
                                                                  1
                                                                  down vote










                                                                  up vote
                                                                  1
                                                                  down vote









                                                                  We use Immer https://github.com/mweststrate/immer to handle these kinds of issues.



                                                                  Just replaced this code in one of our components



                                                                  this.setState(prevState => ({
                                                                  ...prevState,
                                                                  preferences: {
                                                                  ...prevState.preferences,
                                                                  [key]: newValue
                                                                  }
                                                                  }));


                                                                  With this



                                                                  import produce from 'immer';

                                                                  this.setState(produce(draft => {
                                                                  draft.preferences[key] = newValue;
                                                                  }));


                                                                  With immer you handle your state as a "normal object".
                                                                  The magic happens behind the scene with proxy objects.






                                                                  share|improve this answer












                                                                  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.







                                                                  share|improve this answer












                                                                  share|improve this answer



                                                                  share|improve this answer










                                                                  answered Aug 24 at 9:40









                                                                  Joakim Jäderberg

                                                                  111




                                                                  111






















                                                                      up vote
                                                                      1
                                                                      down vote













                                                                      Create a copy of the state:
                                                                      let someProperty = JSON.parse(JSON.stringify(this.state.someProperty))



                                                                      make changes in this object:
                                                                      someProperty.flag = "false"



                                                                      now update the state

                                                                      this.setState({someProperty})






                                                                      share|improve this answer

























                                                                        up vote
                                                                        1
                                                                        down vote













                                                                        Create a copy of the state:
                                                                        let someProperty = JSON.parse(JSON.stringify(this.state.someProperty))



                                                                        make changes in this object:
                                                                        someProperty.flag = "false"



                                                                        now update the state

                                                                        this.setState({someProperty})






                                                                        share|improve this answer























                                                                          up vote
                                                                          1
                                                                          down vote










                                                                          up vote
                                                                          1
                                                                          down vote









                                                                          Create a copy of the state:
                                                                          let someProperty = JSON.parse(JSON.stringify(this.state.someProperty))



                                                                          make changes in this object:
                                                                          someProperty.flag = "false"



                                                                          now update the state

                                                                          this.setState({someProperty})






                                                                          share|improve this answer












                                                                          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})







                                                                          share|improve this answer












                                                                          share|improve this answer



                                                                          share|improve this answer










                                                                          answered Oct 18 at 9:51









                                                                          Dhakad

                                                                          154




                                                                          154






















                                                                              up vote
                                                                              0
                                                                              down vote













                                                                              I found this to work for me, having a project form in my case where for example you have an id, and a name and I'd rather maintain state for a nested project.



                                                                              return (
                                                                              <div>
                                                                              <h2>Project Details</h2>
                                                                              <form>
                                                                              <Input label="ID" group type="number" value={this.state.project.id} onChange={(event) => this.setState({ project: {...this.state.project, id: event.target.value}})} />
                                                                              <Input label="Name" group type="text" value={this.state.project.name} onChange={(event) => this.setState({ project: {...this.state.project, name: event.target.value}})} />
                                                                              </form>
                                                                              </div>
                                                                              )


                                                                              Let me know!






                                                                              share|improve this answer

























                                                                                up vote
                                                                                0
                                                                                down vote













                                                                                I found this to work for me, having a project form in my case where for example you have an id, and a name and I'd rather maintain state for a nested project.



                                                                                return (
                                                                                <div>
                                                                                <h2>Project Details</h2>
                                                                                <form>
                                                                                <Input label="ID" group type="number" value={this.state.project.id} onChange={(event) => this.setState({ project: {...this.state.project, id: event.target.value}})} />
                                                                                <Input label="Name" group type="text" value={this.state.project.name} onChange={(event) => this.setState({ project: {...this.state.project, name: event.target.value}})} />
                                                                                </form>
                                                                                </div>
                                                                                )


                                                                                Let me know!






                                                                                share|improve this answer























                                                                                  up vote
                                                                                  0
                                                                                  down vote










                                                                                  up vote
                                                                                  0
                                                                                  down vote









                                                                                  I found this to work for me, having a project form in my case where for example you have an id, and a name and I'd rather maintain state for a nested project.



                                                                                  return (
                                                                                  <div>
                                                                                  <h2>Project Details</h2>
                                                                                  <form>
                                                                                  <Input label="ID" group type="number" value={this.state.project.id} onChange={(event) => this.setState({ project: {...this.state.project, id: event.target.value}})} />
                                                                                  <Input label="Name" group type="text" value={this.state.project.name} onChange={(event) => this.setState({ project: {...this.state.project, name: event.target.value}})} />
                                                                                  </form>
                                                                                  </div>
                                                                                  )


                                                                                  Let me know!






                                                                                  share|improve this answer












                                                                                  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!







                                                                                  share|improve this answer












                                                                                  share|improve this answer



                                                                                  share|improve this answer










                                                                                  answered Jun 13 at 15:36









                                                                                  Michael Stokes

                                                                                  133214




                                                                                  133214






















                                                                                      up vote
                                                                                      0
                                                                                      down vote













                                                                                      Something like this might suffice,



                                                                                      const isObject = (thing) => {
                                                                                      if(thing &&
                                                                                      typeof thing === 'object' &&
                                                                                      typeof thing !== null
                                                                                      && !(Array.isArray(thing))
                                                                                      ){
                                                                                      return true;
                                                                                      }
                                                                                      return false;
                                                                                      }

                                                                                      /*
                                                                                      Call with an array containing the path to the property you want to access
                                                                                      And the current component/redux state.

                                                                                      For example if we want to update `hello` within the following obj
                                                                                      const obj = {
                                                                                      somePrimitive:false,
                                                                                      someNestedObj:{
                                                                                      hello:1
                                                                                      }
                                                                                      }

                                                                                      we would do :
                                                                                      //clone the object
                                                                                      const cloned = clone(['someNestedObj','hello'],obj)
                                                                                      //Set the new value
                                                                                      cloned.someNestedObj.hello = 5;

                                                                                      */
                                                                                      const clone = (arr, state) => {
                                                                                      let clonedObj = {...state}
                                                                                      const originalObj = clonedObj;
                                                                                      arr.forEach(property => {
                                                                                      if(!(property in clonedObj)){
                                                                                      throw new Error('State missing property')
                                                                                      }

                                                                                      if(isObject(clonedObj[property])){
                                                                                      clonedObj[property] = {...originalObj[property]};
                                                                                      clonedObj = clonedObj[property];
                                                                                      }
                                                                                      })
                                                                                      return originalObj;
                                                                                      }

                                                                                      const nestedObj = {
                                                                                      someProperty:true,
                                                                                      someNestedObj:{
                                                                                      someOtherProperty:true
                                                                                      }
                                                                                      }

                                                                                      const clonedObj = clone(['someProperty'], nestedObj);
                                                                                      console.log(clonedObj === nestedObj) //returns false
                                                                                      console.log(clonedObj.someProperty === nestedObj.someProperty) //returns true
                                                                                      console.log(clonedObj.someNestedObj === nestedObj.someNestedObj) //returns true

                                                                                      console.log()
                                                                                      const clonedObj2 = clone(['someProperty','someNestedObj','someOtherProperty'], nestedObj);
                                                                                      console.log(clonedObj2 === nestedObj) // returns false
                                                                                      console.log(clonedObj2.someNestedObj === nestedObj.someNestedObj) //returns false
                                                                                      //returns true (doesn't attempt to clone because its primitive type)
                                                                                      console.log(clonedObj2.someNestedObj.someOtherProperty === nestedObj.someNestedObj.someOtherProperty)





                                                                                      share|improve this answer

























                                                                                        up vote
                                                                                        0
                                                                                        down vote













                                                                                        Something like this might suffice,



                                                                                        const isObject = (thing) => {
                                                                                        if(thing &&
                                                                                        typeof thing === 'object' &&
                                                                                        typeof thing !== null
                                                                                        && !(Array.isArray(thing))
                                                                                        ){
                                                                                        return true;
                                                                                        }
                                                                                        return false;
                                                                                        }

                                                                                        /*
                                                                                        Call with an array containing the path to the property you want to access
                                                                                        And the current component/redux state.

                                                                                        For example if we want to update `hello` within the following obj
                                                                                        const obj = {
                                                                                        somePrimitive:false,
                                                                                        someNestedObj:{
                                                                                        hello:1
                                                                                        }
                                                                                        }

                                                                                        we would do :
                                                                                        //clone the object
                                                                                        const cloned = clone(['someNestedObj','hello'],obj)
                                                                                        //Set the new value
                                                                                        cloned.someNestedObj.hello = 5;

                                                                                        */
                                                                                        const clone = (arr, state) => {
                                                                                        let clonedObj = {...state}
                                                                                        const originalObj = clonedObj;
                                                                                        arr.forEach(property => {
                                                                                        if(!(property in clonedObj)){
                                                                                        throw new Error('State missing property')
                                                                                        }

                                                                                        if(isObject(clonedObj[property])){
                                                                                        clonedObj[property] = {...originalObj[property]};
                                                                                        clonedObj = clonedObj[property];
                                                                                        }
                                                                                        })
                                                                                        return originalObj;
                                                                                        }

                                                                                        const nestedObj = {
                                                                                        someProperty:true,
                                                                                        someNestedObj:{
                                                                                        someOtherProperty:true
                                                                                        }
                                                                                        }

                                                                                        const clonedObj = clone(['someProperty'], nestedObj);
                                                                                        console.log(clonedObj === nestedObj) //returns false
                                                                                        console.log(clonedObj.someProperty === nestedObj.someProperty) //returns true
                                                                                        console.log(clonedObj.someNestedObj === nestedObj.someNestedObj) //returns true

                                                                                        console.log()
                                                                                        const clonedObj2 = clone(['someProperty','someNestedObj','someOtherProperty'], nestedObj);
                                                                                        console.log(clonedObj2 === nestedObj) // returns false
                                                                                        console.log(clonedObj2.someNestedObj === nestedObj.someNestedObj) //returns false
                                                                                        //returns true (doesn't attempt to clone because its primitive type)
                                                                                        console.log(clonedObj2.someNestedObj.someOtherProperty === nestedObj.someNestedObj.someOtherProperty)





                                                                                        share|improve this answer























                                                                                          up vote
                                                                                          0
                                                                                          down vote










                                                                                          up vote
                                                                                          0
                                                                                          down vote









                                                                                          Something like this might suffice,



                                                                                          const isObject = (thing) => {
                                                                                          if(thing &&
                                                                                          typeof thing === 'object' &&
                                                                                          typeof thing !== null
                                                                                          && !(Array.isArray(thing))
                                                                                          ){
                                                                                          return true;
                                                                                          }
                                                                                          return false;
                                                                                          }

                                                                                          /*
                                                                                          Call with an array containing the path to the property you want to access
                                                                                          And the current component/redux state.

                                                                                          For example if we want to update `hello` within the following obj
                                                                                          const obj = {
                                                                                          somePrimitive:false,
                                                                                          someNestedObj:{
                                                                                          hello:1
                                                                                          }
                                                                                          }

                                                                                          we would do :
                                                                                          //clone the object
                                                                                          const cloned = clone(['someNestedObj','hello'],obj)
                                                                                          //Set the new value
                                                                                          cloned.someNestedObj.hello = 5;

                                                                                          */
                                                                                          const clone = (arr, state) => {
                                                                                          let clonedObj = {...state}
                                                                                          const originalObj = clonedObj;
                                                                                          arr.forEach(property => {
                                                                                          if(!(property in clonedObj)){
                                                                                          throw new Error('State missing property')
                                                                                          }

                                                                                          if(isObject(clonedObj[property])){
                                                                                          clonedObj[property] = {...originalObj[property]};
                                                                                          clonedObj = clonedObj[property];
                                                                                          }
                                                                                          })
                                                                                          return originalObj;
                                                                                          }

                                                                                          const nestedObj = {
                                                                                          someProperty:true,
                                                                                          someNestedObj:{
                                                                                          someOtherProperty:true
                                                                                          }
                                                                                          }

                                                                                          const clonedObj = clone(['someProperty'], nestedObj);
                                                                                          console.log(clonedObj === nestedObj) //returns false
                                                                                          console.log(clonedObj.someProperty === nestedObj.someProperty) //returns true
                                                                                          console.log(clonedObj.someNestedObj === nestedObj.someNestedObj) //returns true

                                                                                          console.log()
                                                                                          const clonedObj2 = clone(['someProperty','someNestedObj','someOtherProperty'], nestedObj);
                                                                                          console.log(clonedObj2 === nestedObj) // returns false
                                                                                          console.log(clonedObj2.someNestedObj === nestedObj.someNestedObj) //returns false
                                                                                          //returns true (doesn't attempt to clone because its primitive type)
                                                                                          console.log(clonedObj2.someNestedObj.someOtherProperty === nestedObj.someNestedObj.someOtherProperty)





                                                                                          share|improve this answer












                                                                                          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)






                                                                                          share|improve this answer












                                                                                          share|improve this answer



                                                                                          share|improve this answer










                                                                                          answered Aug 29 at 4:28









                                                                                          Eladian

                                                                                          485316




                                                                                          485316






















                                                                                              up vote
                                                                                              0
                                                                                              down vote













                                                                                              I take very seriously the concerns already voiced around creating a complete copy of your component state. That being said, if compromised performance is not of paramount concern (i.e. you don't have a very deep tree of components which React would want to re-render) I would suggest Immer.



                                                                                              import produce from 'immer';

                                                                                              <Input
                                                                                              value={this.state.form.username}
                                                                                              onChange={e => produce(this.state, s => { s.form.username = e.target.value }) } />


                                                                                              This should work for React.PureComponent (i.e. shallow state comparisons by React) as Immer cleverly uses a proxy object to efficiently copy an arbitrarily deep state tree. Immer is also more typesafe compared to libraries like Immutability Helper, and is ideal for Javascript and Typescript users alike.





                                                                                              Typescript utility function



                                                                                              function setStateDeep<S>(comp: React.Component<any, S, any>, fn: (s: 
                                                                                              Draft<Readonly<S>>) => any) {
                                                                                              comp.setState(produce(comp.state, s => { fn(s); }))
                                                                                              }

                                                                                              onChange={e => setStateDeep(this, s => s.form.username = e.target.value)}





                                                                                              share|improve this answer



























                                                                                                up vote
                                                                                                0
                                                                                                down vote













                                                                                                I take very seriously the concerns already voiced around creating a complete copy of your component state. That being said, if compromised performance is not of paramount concern (i.e. you don't have a very deep tree of components which React would want to re-render) I would suggest Immer.



                                                                                                import produce from 'immer';

                                                                                                <Input
                                                                                                value={this.state.form.username}
                                                                                                onChange={e => produce(this.state, s => { s.form.username = e.target.value }) } />


                                                                                                This should work for React.PureComponent (i.e. shallow state comparisons by React) as Immer cleverly uses a proxy object to efficiently copy an arbitrarily deep state tree. Immer is also more typesafe compared to libraries like Immutability Helper, and is ideal for Javascript and Typescript users alike.





                                                                                                Typescript utility function



                                                                                                function setStateDeep<S>(comp: React.Component<any, S, any>, fn: (s: 
                                                                                                Draft<Readonly<S>>) => any) {
                                                                                                comp.setState(produce(comp.state, s => { fn(s); }))
                                                                                                }

                                                                                                onChange={e => setStateDeep(this, s => s.form.username = e.target.value)}





                                                                                                share|improve this answer

























                                                                                                  up vote
                                                                                                  0
                                                                                                  down vote










                                                                                                  up vote
                                                                                                  0
                                                                                                  down vote









                                                                                                  I take very seriously the concerns already voiced around creating a complete copy of your component state. That being said, if compromised performance is not of paramount concern (i.e. you don't have a very deep tree of components which React would want to re-render) I would suggest Immer.



                                                                                                  import produce from 'immer';

                                                                                                  <Input
                                                                                                  value={this.state.form.username}
                                                                                                  onChange={e => produce(this.state, s => { s.form.username = e.target.value }) } />


                                                                                                  This should work for React.PureComponent (i.e. shallow state comparisons by React) as Immer cleverly uses a proxy object to efficiently copy an arbitrarily deep state tree. Immer is also more typesafe compared to libraries like Immutability Helper, and is ideal for Javascript and Typescript users alike.





                                                                                                  Typescript utility function



                                                                                                  function setStateDeep<S>(comp: React.Component<any, S, any>, fn: (s: 
                                                                                                  Draft<Readonly<S>>) => any) {
                                                                                                  comp.setState(produce(comp.state, s => { fn(s); }))
                                                                                                  }

                                                                                                  onChange={e => setStateDeep(this, s => s.form.username = e.target.value)}





                                                                                                  share|improve this answer














                                                                                                  I take very seriously the concerns already voiced around creating a complete copy of your component state. That being said, if compromised performance is not of paramount concern (i.e. you don't have a very deep tree of components which React would want to re-render) I would suggest Immer.



                                                                                                  import produce from 'immer';

                                                                                                  <Input
                                                                                                  value={this.state.form.username}
                                                                                                  onChange={e => produce(this.state, s => { s.form.username = e.target.value }) } />


                                                                                                  This should work for React.PureComponent (i.e. shallow state comparisons by React) as Immer cleverly uses a proxy object to efficiently copy an arbitrarily deep state tree. Immer is also more typesafe compared to libraries like Immutability Helper, and is ideal for Javascript and Typescript users alike.





                                                                                                  Typescript utility function



                                                                                                  function setStateDeep<S>(comp: React.Component<any, S, any>, fn: (s: 
                                                                                                  Draft<Readonly<S>>) => any) {
                                                                                                  comp.setState(produce(comp.state, s => { fn(s); }))
                                                                                                  }

                                                                                                  onChange={e => setStateDeep(this, s => s.form.username = e.target.value)}






                                                                                                  share|improve this answer














                                                                                                  share|improve this answer



                                                                                                  share|improve this answer








                                                                                                  edited Nov 13 at 7:51

























                                                                                                  answered Nov 13 at 7:02









                                                                                                  Stephen Paul

                                                                                                  14.3k84644




                                                                                                  14.3k84644






























                                                                                                       

                                                                                                      draft saved


                                                                                                      draft discarded



















































                                                                                                       


                                                                                                      draft saved


                                                                                                      draft discarded














                                                                                                      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





















































                                                                                                      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







                                                                                                      Popular posts from this blog

                                                                                                      Biblatex bibliography style without URLs when DOI exists (in Overleaf with Zotero bibliography)

                                                                                                      ComboBox Display Member on multiple fields

                                                                                                      Is it possible to collect Nectar points via Trainline?