How to update nested state properties in React












140















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 '18 at 8:57













  • nested state is bad programming style in React JS

    – Troyan Victor
    Oct 5 '18 at 8:30
















140















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 '18 at 8:57













  • nested state is bad programming style in React JS

    – Troyan Victor
    Oct 5 '18 at 8:30














140












140








140


50






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 '18 at 0:29









Dan Dascalescu

66.5k21201274




66.5k21201274










asked Mar 27 '17 at 7:51









Alex YongAlex Yong

1,0552721




1,0552721








  • 2





    What do you mean doesn't work? This is not a very good question - what happened? Any errors? What errors?

    – Darren Sweeney
    Mar 27 '17 at 7:53











  • Try to read: stackoverflow.com/questions/18933985/…

    – Andrew Paramoshkin
    Mar 27 '17 at 7:58






  • 4





    The answer is Don't use Nested State in React. Read this excellent answer.

    – Qwerty
    Aug 10 '18 at 8:57













  • nested state is bad programming style in React JS

    – Troyan Victor
    Oct 5 '18 at 8:30














  • 2





    What do you mean doesn't work? This is not a very good question - what happened? Any errors? What errors?

    – Darren Sweeney
    Mar 27 '17 at 7:53











  • Try to read: stackoverflow.com/questions/18933985/…

    – Andrew Paramoshkin
    Mar 27 '17 at 7:58






  • 4





    The answer is Don't use Nested State in React. Read this excellent answer.

    – Qwerty
    Aug 10 '18 at 8:57













  • nested state is bad programming style in React JS

    – Troyan Victor
    Oct 5 '18 at 8:30








2




2





What do you mean doesn't work? This is not a very good question - what happened? Any errors? What errors?

– Darren Sweeney
Mar 27 '17 at 7:53





What do you mean doesn't work? This is not a very good question - what happened? Any errors? What errors?

– Darren Sweeney
Mar 27 '17 at 7:53













Try to read: stackoverflow.com/questions/18933985/…

– Andrew Paramoshkin
Mar 27 '17 at 7:58





Try to read: stackoverflow.com/questions/18933985/…

– Andrew Paramoshkin
Mar 27 '17 at 7:58




4




4





The answer is Don't use Nested State in React. Read this excellent answer.

– Qwerty
Aug 10 '18 at 8:57







The answer is Don't use Nested State in React. Read this excellent answer.

– Qwerty
Aug 10 '18 at 8:57















nested state is bad programming style in React JS

– Troyan Victor
Oct 5 '18 at 8:30





nested state is bad programming style in React JS

– Troyan Victor
Oct 5 '18 at 8:30












16 Answers
16






active

oldest

votes


















182














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






  • 5





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

    – olejorgenb
    Dec 21 '17 at 20:55






  • 1





    I don't think you need the ...prevState, in the last example, you only someProperty and its children

    – Jemar Jones
    May 25 '18 at 19:43



















75














To write it in one line



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





share|improve this answer



















  • 6





    It is recommended to use the updater for setState instead of directly accessing this.state within setState. reactjs.org/docs/react-component.html#setstate

    – Raghu Teja
    May 23 '18 at 12:52






  • 2





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

    – trpt4him
    Jun 1 '18 at 13:06






  • 1





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

    – Yoseph
    Jun 4 '18 at 4:27





















35














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 '18 at 13:45











  • Shallow comparison is used for pure components by default.

    – Basel Shishani
    Sep 26 '18 at 12:10






  • 1





    I want to create an altar in your honor and pray to it every morning. It took me three days to reach this answer that perfectly explains the OBVIOUS design decision. Everyone just tries to use the spread operator or do other hacks just because nested state looks more human-readable.

    – Edeph
    Nov 29 '18 at 14:06



















23














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





















  • 7





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

    – SpyMaster356
    May 12 '18 at 16:51











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

    – Qwerty
    Aug 9 '18 at 9:44













  • Also I added an example react-experiments.herokuapp.com/state-flow (link to git at the top)

    – Qwerty
    Aug 9 '18 at 16:28











  • What if you load for example an object with nested attributes from a database into state? How to avoid nested state in that case?

    – tobiv
    Jan 7 at 0:17






  • 1





    @tobiv It's better to transform the data upon a fetch into a different structure, but it depends on the usecase. It's okay to have a nested object or array of objects in a state if you think of them as compact units of some data. The problem arises when you need to store UI switches or other observable data (i.e. data that should immediately change the view) as they must be flat in order to correctly trigger internal React diffing algorithms. For changes in other nested objects, it's easy to trigger this.forceUpdate() or implement specific shouldComponentUpdate.

    – Qwerty
    Jan 7 at 17:44





















19














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





















  • 9





    And I could be wrong but I don't think you're using React without ES2015.

    – Madbreaks
    Nov 10 '17 at 18:29





















7














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 '18 at 20:37











  • @YixingLiu: I've updated the answer to use lodash/fp, so we don't have to worry about mutations.

    – tokland
    Jul 26 '18 at 20:50





















5














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































    5














    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































      3














      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































        2














        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































          2














          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

































            1














            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































              1














              I take very seriously the concerns already voiced around creating a complete copy of your component state. With that said, I would strongly suggest Immer.



              import produce from 'immer';

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


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





              Typescript utility function



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

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





              share|improve this answer

































                0














                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































                  0














                  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































                    0














                    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























                      Your Answer






                      StackExchange.ifUsing("editor", function () {
                      StackExchange.using("externalEditor", function () {
                      StackExchange.using("snippets", function () {
                      StackExchange.snippets.init();
                      });
                      });
                      }, "code-snippets");

                      StackExchange.ready(function() {
                      var channelOptions = {
                      tags: "".split(" "),
                      id: "1"
                      };
                      initTagRenderer("".split(" "), "".split(" "), channelOptions);

                      StackExchange.using("externalEditor", function() {
                      // Have to fire editor after snippets, if snippets enabled
                      if (StackExchange.settings.snippets.snippetsEnabled) {
                      StackExchange.using("snippets", function() {
                      createEditor();
                      });
                      }
                      else {
                      createEditor();
                      }
                      });

                      function createEditor() {
                      StackExchange.prepareEditor({
                      heartbeatType: 'answer',
                      autoActivateHeartbeat: false,
                      convertImagesToLinks: true,
                      noModals: true,
                      showLowRepImageUploadWarning: true,
                      reputationToPostImages: 10,
                      bindNavPrevention: true,
                      postfix: "",
                      imageUploader: {
                      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
                      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
                      allowUrls: true
                      },
                      onDemand: true,
                      discardSelector: ".discard-answer"
                      ,immediatelyShowMarkdownHelp:true
                      });


                      }
                      });














                      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









                      182














                      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






                      • 5





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

                        – olejorgenb
                        Dec 21 '17 at 20:55






                      • 1





                        I don't think you need the ...prevState, in the last example, you only someProperty and its children

                        – Jemar Jones
                        May 25 '18 at 19:43
















                      182














                      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






                      • 5





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

                        – olejorgenb
                        Dec 21 '17 at 20:55






                      • 1





                        I don't think you need the ...prevState, in the last example, you only someProperty and its children

                        – Jemar Jones
                        May 25 '18 at 19:43














                      182












                      182








                      182







                      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 '18 at 18:55









                      Bastiaan ten Klooster

                      13812




                      13812










                      answered Mar 27 '17 at 8:25









                      Shubham KhatriShubham Khatri

                      81.2k1598137




                      81.2k1598137








                      • 1





                        @Ohgodwhy , no this is not accessing the state directly since {...this.state.someProperty} retuns a new obejct 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






                      • 5





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

                        – olejorgenb
                        Dec 21 '17 at 20:55






                      • 1





                        I don't think you need the ...prevState, in the last example, you only someProperty and its children

                        – Jemar Jones
                        May 25 '18 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






                      • 5





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

                        – olejorgenb
                        Dec 21 '17 at 20:55






                      • 1





                        I don't think you need the ...prevState, in the last example, you only someProperty and its children

                        – Jemar Jones
                        May 25 '18 at 19:43








                      1




                      1





                      @Ohgodwhy , no this is not accessing the state directly since {...this.state.someProperty} retuns a new obejct to someProperty and we are modifying that

                      – Shubham Khatri
                      Apr 15 '17 at 9:17





                      @Ohgodwhy , no this is not accessing the state directly since {...this.state.someProperty} retuns a new obejct to someProperty and we are modifying that

                      – Shubham Khatri
                      Apr 15 '17 at 9:17




                      2




                      2





                      this didn't work for me.. i'm using react version @15.6.1

                      – Rajiv
                      Jun 30 '17 at 20:36





                      this didn't work for me.. i'm using react version @15.6.1

                      – Rajiv
                      Jun 30 '17 at 20:36




                      2




                      2





                      @Stophface We can use Lodash to deep clone sure, but not everyone would include this library just to setState

                      – Shubham Khatri
                      Nov 23 '17 at 5:36





                      @Stophface We can use Lodash to deep clone sure, but not everyone would include this library just to setState

                      – Shubham Khatri
                      Nov 23 '17 at 5:36




                      5




                      5





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

                      – olejorgenb
                      Dec 21 '17 at 20:55





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

                      – olejorgenb
                      Dec 21 '17 at 20:55




                      1




                      1





                      I don't think you need the ...prevState, in the last example, you only someProperty and its children

                      – Jemar Jones
                      May 25 '18 at 19:43





                      I don't think you need the ...prevState, in the last example, you only someProperty and its children

                      – Jemar Jones
                      May 25 '18 at 19:43













                      75














                      To write it in one line



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





                      share|improve this answer



















                      • 6





                        It is recommended to use the updater for setState instead of directly accessing this.state within setState. reactjs.org/docs/react-component.html#setstate

                        – Raghu Teja
                        May 23 '18 at 12:52






                      • 2





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

                        – trpt4him
                        Jun 1 '18 at 13:06






                      • 1





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

                        – Yoseph
                        Jun 4 '18 at 4:27


















                      75














                      To write it in one line



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





                      share|improve this answer



















                      • 6





                        It is recommended to use the updater for setState instead of directly accessing this.state within setState. reactjs.org/docs/react-component.html#setstate

                        – Raghu Teja
                        May 23 '18 at 12:52






                      • 2





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

                        – trpt4him
                        Jun 1 '18 at 13:06






                      • 1





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

                        – Yoseph
                        Jun 4 '18 at 4:27
















                      75












                      75








                      75







                      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









                      YosephYoseph

                      1,128813




                      1,128813








                      • 6





                        It is recommended to use the updater for setState instead of directly accessing this.state within setState. reactjs.org/docs/react-component.html#setstate

                        – Raghu Teja
                        May 23 '18 at 12:52






                      • 2





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

                        – trpt4him
                        Jun 1 '18 at 13:06






                      • 1





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

                        – Yoseph
                        Jun 4 '18 at 4:27
















                      • 6





                        It is recommended to use the updater for setState instead of directly accessing this.state within setState. reactjs.org/docs/react-component.html#setstate

                        – Raghu Teja
                        May 23 '18 at 12:52






                      • 2





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

                        – trpt4him
                        Jun 1 '18 at 13:06






                      • 1





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

                        – Yoseph
                        Jun 4 '18 at 4:27










                      6




                      6





                      It is recommended to use the updater for setState instead of directly accessing this.state within setState. reactjs.org/docs/react-component.html#setstate

                      – Raghu Teja
                      May 23 '18 at 12:52





                      It is recommended to use the updater for setState instead of directly accessing this.state within setState. reactjs.org/docs/react-component.html#setstate

                      – Raghu Teja
                      May 23 '18 at 12:52




                      2




                      2





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

                      – trpt4him
                      Jun 1 '18 at 13:06





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

                      – trpt4him
                      Jun 1 '18 at 13:06




                      1




                      1





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

                      – Yoseph
                      Jun 4 '18 at 4:27







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

                      – Yoseph
                      Jun 4 '18 at 4:27













                      35














                      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 '18 at 13:45











                      • Shallow comparison is used for pure components by default.

                        – Basel Shishani
                        Sep 26 '18 at 12:10






                      • 1





                        I want to create an altar in your honor and pray to it every morning. It took me three days to reach this answer that perfectly explains the OBVIOUS design decision. Everyone just tries to use the spread operator or do other hacks just because nested state looks more human-readable.

                        – Edeph
                        Nov 29 '18 at 14:06
















                      35














                      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 '18 at 13:45











                      • Shallow comparison is used for pure components by default.

                        – Basel Shishani
                        Sep 26 '18 at 12:10






                      • 1





                        I want to create an altar in your honor and pray to it every morning. It took me three days to reach this answer that perfectly explains the OBVIOUS design decision. Everyone just tries to use the spread operator or do other hacks just because nested state looks more human-readable.

                        – Edeph
                        Nov 29 '18 at 14:06














                      35












                      35








                      35







                      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 '18 at 12:18









                      Konstantin SmolyaninKonstantin Smolyanin

                      11.4k54132




                      11.4k54132













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

                        – pors
                        Aug 30 '18 at 13:45











                      • Shallow comparison is used for pure components by default.

                        – Basel Shishani
                        Sep 26 '18 at 12:10






                      • 1





                        I want to create an altar in your honor and pray to it every morning. It took me three days to reach this answer that perfectly explains the OBVIOUS design decision. Everyone just tries to use the spread operator or do other hacks just because nested state looks more human-readable.

                        – Edeph
                        Nov 29 '18 at 14:06



















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

                        – pors
                        Aug 30 '18 at 13:45











                      • Shallow comparison is used for pure components by default.

                        – Basel Shishani
                        Sep 26 '18 at 12:10






                      • 1





                        I want to create an altar in your honor and pray to it every morning. It took me three days to reach this answer that perfectly explains the OBVIOUS design decision. Everyone just tries to use the spread operator or do other hacks just because nested state looks more human-readable.

                        – Edeph
                        Nov 29 '18 at 14:06

















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

                      – pors
                      Aug 30 '18 at 13:45





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

                      – pors
                      Aug 30 '18 at 13:45













                      Shallow comparison is used for pure components by default.

                      – Basel Shishani
                      Sep 26 '18 at 12:10





                      Shallow comparison is used for pure components by default.

                      – Basel Shishani
                      Sep 26 '18 at 12:10




                      1




                      1





                      I want to create an altar in your honor and pray to it every morning. It took me three days to reach this answer that perfectly explains the OBVIOUS design decision. Everyone just tries to use the spread operator or do other hacks just because nested state looks more human-readable.

                      – Edeph
                      Nov 29 '18 at 14:06





                      I want to create an altar in your honor and pray to it every morning. It took me three days to reach this answer that perfectly explains the OBVIOUS design decision. Everyone just tries to use the spread operator or do other hacks just because nested state looks more human-readable.

                      – Edeph
                      Nov 29 '18 at 14:06











                      23














                      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





















                      • 7





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

                        – SpyMaster356
                        May 12 '18 at 16:51











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

                        – Qwerty
                        Aug 9 '18 at 9:44













                      • Also I added an example react-experiments.herokuapp.com/state-flow (link to git at the top)

                        – Qwerty
                        Aug 9 '18 at 16:28











                      • What if you load for example an object with nested attributes from a database into state? How to avoid nested state in that case?

                        – tobiv
                        Jan 7 at 0:17






                      • 1





                        @tobiv It's better to transform the data upon a fetch into a different structure, but it depends on the usecase. It's okay to have a nested object or array of objects in a state if you think of them as compact units of some data. The problem arises when you need to store UI switches or other observable data (i.e. data that should immediately change the view) as they must be flat in order to correctly trigger internal React diffing algorithms. For changes in other nested objects, it's easy to trigger this.forceUpdate() or implement specific shouldComponentUpdate.

                        – Qwerty
                        Jan 7 at 17:44


















                      23














                      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





















                      • 7





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

                        – SpyMaster356
                        May 12 '18 at 16:51











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

                        – Qwerty
                        Aug 9 '18 at 9:44













                      • Also I added an example react-experiments.herokuapp.com/state-flow (link to git at the top)

                        – Qwerty
                        Aug 9 '18 at 16:28











                      • What if you load for example an object with nested attributes from a database into state? How to avoid nested state in that case?

                        – tobiv
                        Jan 7 at 0:17






                      • 1





                        @tobiv It's better to transform the data upon a fetch into a different structure, but it depends on the usecase. It's okay to have a nested object or array of objects in a state if you think of them as compact units of some data. The problem arises when you need to store UI switches or other observable data (i.e. data that should immediately change the view) as they must be flat in order to correctly trigger internal React diffing algorithms. For changes in other nested objects, it's easy to trigger this.forceUpdate() or implement specific shouldComponentUpdate.

                        – Qwerty
                        Jan 7 at 17:44
















                      23












                      23








                      23







                      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 '18 at 10:04

























                      answered May 3 '18 at 7:50









                      QwertyQwerty

                      9,27775271




                      9,27775271








                      • 7





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

                        – SpyMaster356
                        May 12 '18 at 16:51











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

                        – Qwerty
                        Aug 9 '18 at 9:44













                      • Also I added an example react-experiments.herokuapp.com/state-flow (link to git at the top)

                        – Qwerty
                        Aug 9 '18 at 16:28











                      • What if you load for example an object with nested attributes from a database into state? How to avoid nested state in that case?

                        – tobiv
                        Jan 7 at 0:17






                      • 1





                        @tobiv It's better to transform the data upon a fetch into a different structure, but it depends on the usecase. It's okay to have a nested object or array of objects in a state if you think of them as compact units of some data. The problem arises when you need to store UI switches or other observable data (i.e. data that should immediately change the view) as they must be flat in order to correctly trigger internal React diffing algorithms. For changes in other nested objects, it's easy to trigger this.forceUpdate() or implement specific shouldComponentUpdate.

                        – Qwerty
                        Jan 7 at 17:44
















                      • 7





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

                        – SpyMaster356
                        May 12 '18 at 16:51











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

                        – Qwerty
                        Aug 9 '18 at 9:44













                      • Also I added an example react-experiments.herokuapp.com/state-flow (link to git at the top)

                        – Qwerty
                        Aug 9 '18 at 16:28











                      • What if you load for example an object with nested attributes from a database into state? How to avoid nested state in that case?

                        – tobiv
                        Jan 7 at 0:17






                      • 1





                        @tobiv It's better to transform the data upon a fetch into a different structure, but it depends on the usecase. It's okay to have a nested object or array of objects in a state if you think of them as compact units of some data. The problem arises when you need to store UI switches or other observable data (i.e. data that should immediately change the view) as they must be flat in order to correctly trigger internal React diffing algorithms. For changes in other nested objects, it's easy to trigger this.forceUpdate() or implement specific shouldComponentUpdate.

                        – Qwerty
                        Jan 7 at 17:44










                      7




                      7





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

                      – SpyMaster356
                      May 12 '18 at 16:51





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

                      – SpyMaster356
                      May 12 '18 at 16:51













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

                      – Qwerty
                      Aug 9 '18 at 9:44







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

                      – Qwerty
                      Aug 9 '18 at 9:44















                      Also I added an example react-experiments.herokuapp.com/state-flow (link to git at the top)

                      – Qwerty
                      Aug 9 '18 at 16:28





                      Also I added an example react-experiments.herokuapp.com/state-flow (link to git at the top)

                      – Qwerty
                      Aug 9 '18 at 16:28













                      What if you load for example an object with nested attributes from a database into state? How to avoid nested state in that case?

                      – tobiv
                      Jan 7 at 0:17





                      What if you load for example an object with nested attributes from a database into state? How to avoid nested state in that case?

                      – tobiv
                      Jan 7 at 0:17




                      1




                      1





                      @tobiv It's better to transform the data upon a fetch into a different structure, but it depends on the usecase. It's okay to have a nested object or array of objects in a state if you think of them as compact units of some data. The problem arises when you need to store UI switches or other observable data (i.e. data that should immediately change the view) as they must be flat in order to correctly trigger internal React diffing algorithms. For changes in other nested objects, it's easy to trigger this.forceUpdate() or implement specific shouldComponentUpdate.

                      – Qwerty
                      Jan 7 at 17:44







                      @tobiv It's better to transform the data upon a fetch into a different structure, but it depends on the usecase. It's okay to have a nested object or array of objects in a state if you think of them as compact units of some data. The problem arises when you need to store UI switches or other observable data (i.e. data that should immediately change the view) as they must be flat in order to correctly trigger internal React diffing algorithms. For changes in other nested objects, it's easy to trigger this.forceUpdate() or implement specific shouldComponentUpdate.

                      – Qwerty
                      Jan 7 at 17:44













                      19














                      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





















                      • 9





                        And I could be wrong but I don't think you're using React without ES2015.

                        – Madbreaks
                        Nov 10 '17 at 18:29


















                      19














                      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





















                      • 9





                        And I could be wrong but I don't think you're using React without ES2015.

                        – Madbreaks
                        Nov 10 '17 at 18:29
















                      19












                      19








                      19







                      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 '18 at 6:58

























                      answered Oct 7 '17 at 19:20









                      Alyssa RooseAlyssa Roose

                      19915




                      19915








                      • 9





                        And I could be wrong but I don't think you're using React without ES2015.

                        – Madbreaks
                        Nov 10 '17 at 18:29
















                      • 9





                        And I could be wrong but I don't think you're using React without ES2015.

                        – Madbreaks
                        Nov 10 '17 at 18:29










                      9




                      9





                      And I could be wrong but I don't think you're using React without ES2015.

                      – Madbreaks
                      Nov 10 '17 at 18:29







                      And I could be wrong but I don't think you're using React without ES2015.

                      – Madbreaks
                      Nov 10 '17 at 18:29













                      7














                      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 '18 at 20:37











                      • @YixingLiu: I've updated the answer to use lodash/fp, so we don't have to worry about mutations.

                        – tokland
                        Jul 26 '18 at 20:50


















                      7














                      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 '18 at 20:37











                      • @YixingLiu: I've updated the answer to use lodash/fp, so we don't have to worry about mutations.

                        – tokland
                        Jul 26 '18 at 20:50
















                      7












                      7








                      7







                      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 '18 at 18:42

























                      answered May 19 '17 at 8:15









                      toklandtokland

                      51.8k11115143




                      51.8k11115143













                      • If you use lodash, you probably want to try _.cloneDeep and set state to be the cloned copy.

                        – Yixing Liu
                        Jul 26 '18 at 20:37











                      • @YixingLiu: I've updated the answer to use lodash/fp, so we don't have to worry about mutations.

                        – tokland
                        Jul 26 '18 at 20:50





















                      • If you use lodash, you probably want to try _.cloneDeep and set state to be the cloned copy.

                        – Yixing Liu
                        Jul 26 '18 at 20:37











                      • @YixingLiu: I've updated the answer to use lodash/fp, so we don't have to worry about mutations.

                        – tokland
                        Jul 26 '18 at 20:50



















                      If you use lodash, you probably want to try _.cloneDeep and set state to be the cloned copy.

                      – Yixing Liu
                      Jul 26 '18 at 20:37





                      If you use lodash, you probably want to try _.cloneDeep and set state to be the cloned copy.

                      – Yixing Liu
                      Jul 26 '18 at 20:37













                      @YixingLiu: I've updated the answer to use lodash/fp, so we don't have to worry about mutations.

                      – tokland
                      Jul 26 '18 at 20:50







                      @YixingLiu: I've updated the answer to use lodash/fp, so we don't have to worry about mutations.

                      – tokland
                      Jul 26 '18 at 20:50













                      5














                      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




























                        5














                        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


























                          5












                          5








                          5







                          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 BerkompasMatthew Berkompas

                          6112




                          6112























                              5














                              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




























                                5














                                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


























                                  5












                                  5








                                  5







                                  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 '18 at 13:30









                                  enzolitoenzolito

                                  7817




                                  7817























                                      3














                                      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




























                                        3














                                        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


























                                          3












                                          3








                                          3







                                          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 '18 at 9:40









                                          Joakim JäderbergJoakim Jäderberg

                                          311




                                          311























                                              2














                                              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




























                                                2














                                                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


























                                                  2












                                                  2








                                                  2







                                                  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 '18 at 9:40









                                                  Alberto PirasAlberto Piras

                                                  9113




                                                  9113























                                                      2














                                                      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






























                                                        2














                                                        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




























                                                          2












                                                          2








                                                          2







                                                          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 '18 at 2:57

























                                                          answered Jun 28 '18 at 2:51









                                                          Cory HouseCory House

                                                          7,95275776




                                                          7,95275776























                                                              1














                                                              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




























                                                                1














                                                                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


























                                                                  1












                                                                  1








                                                                  1







                                                                  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 '18 at 9:51









                                                                  DhakadDhakad

                                                                  154




                                                                  154























                                                                      1














                                                                      I take very seriously the concerns already voiced around creating a complete copy of your component state. With that said, I would strongly suggest Immer.



                                                                      import produce from 'immer';

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


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





                                                                      Typescript utility function



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

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





                                                                      share|improve this answer






























                                                                        1














                                                                        I take very seriously the concerns already voiced around creating a complete copy of your component state. With that said, I would strongly suggest Immer.



                                                                        import produce from 'immer';

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


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





                                                                        Typescript utility function



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

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





                                                                        share|improve this answer




























                                                                          1












                                                                          1








                                                                          1







                                                                          I take very seriously the concerns already voiced around creating a complete copy of your component state. With that said, I would strongly suggest Immer.



                                                                          import produce from 'immer';

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


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





                                                                          Typescript utility function



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

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





                                                                          share|improve this answer















                                                                          I take very seriously the concerns already voiced around creating a complete copy of your component state. With that said, I would strongly suggest Immer.



                                                                          import produce from 'immer';

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


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





                                                                          Typescript utility function



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

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






                                                                          share|improve this answer














                                                                          share|improve this answer



                                                                          share|improve this answer








                                                                          edited yesterday

























                                                                          answered Nov 13 '18 at 7:02









                                                                          Stephen PaulStephen Paul

                                                                          15.2k84845




                                                                          15.2k84845























                                                                              0














                                                                              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




























                                                                                0














                                                                                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


























                                                                                  0












                                                                                  0








                                                                                  0







                                                                                  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 '18 at 15:36









                                                                                  Michael StokesMichael Stokes

                                                                                  153314




                                                                                  153314























                                                                                      0














                                                                                      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




























                                                                                        0














                                                                                        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


























                                                                                          0












                                                                                          0








                                                                                          0







                                                                                          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 '18 at 8:29









                                                                                          VenugopalVenugopal

                                                                                          1,63711328




                                                                                          1,63711328























                                                                                              0














                                                                                              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




























                                                                                                0














                                                                                                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


























                                                                                                  0












                                                                                                  0








                                                                                                  0







                                                                                                  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 '18 at 4:28









                                                                                                  EladianEladian

                                                                                                  492316




                                                                                                  492316






























                                                                                                      draft saved

                                                                                                      draft discarded




















































                                                                                                      Thanks for contributing an answer to Stack Overflow!


                                                                                                      • Please be sure to answer the question. Provide details and share your research!

                                                                                                      But avoid



                                                                                                      • Asking for help, clarification, or responding to other answers.

                                                                                                      • Making statements based on opinion; back them up with references or personal experience.


                                                                                                      To learn more, see our tips on writing great answers.




                                                                                                      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

                                                                                                      Full-time equivalent

                                                                                                      Bicuculline

                                                                                                      さくらももこ