41

For a plugin I'm using I have to have a state that looks like this:

getInitialState() {
  return {
    invalid: true,
    access: {
      access_code: '',
      zipcode: '',
      password: '',
      confirm: '',
      hospital_id: '',
    },
  }
},

How would I set the state of hospital_id without setting the rest of access?

This seems to remove everything but hospital_id:

this.setState({access: {hospital_id: 1}})
RPL
  • 3,500
  • 2
  • 21
  • 28
Dan G Nelson
  • 862
  • 2
  • 11
  • 30
  • 1
    I hope React comes out with a better way to do this, like `updateState()`, that would only update the keys passed instead of blowing away what's already there. I find all of these workarounds dangerous because it "fails" silently if somebody forgets to merge the new state with the existing state. – Joshua Pinter Jul 16 '17 at 16:56
  • 2
    how is this a nested array? – orszaczky Apr 26 '18 at 07:25
  • It's not, it's a nested object. Vocab isn't my strong suit. – Dan G Nelson Apr 26 '18 at 12:49

6 Answers6

99

You have a few options:

  1. With ECMA6, you can use the Object spread proposal (...) to create copies of objects with updated properties.

    this.setState({
      access: {
        ...this.state.access,
        hospital_id: 1,
      },
    });
    
  2. You can use the native assign function on the Object (Object.assign())

    this.setState({
      access: Object.assign({}, this.state.access, {
        hospital_id: 1,
      }),
    });
    
  3. Or for the shortest version and atomic update:

     this.setState(({access}) => ({access: {
       ...access,
       hospital_id: 1,
     }});
    
  4. And one more option is the updates addon:

    var update = require('react-addons-update');
    // per React docs 
    // https://reactjs.org/docs/update.html 
    // , you may want to change this to 
    // import update from 'immutability-helper';
    this.setState({
      access: update(this.state.access, {
        hospital_id: {$set: 1},
      })
    });
    

I would recommend using the first one.

chris Frisina
  • 19,086
  • 22
  • 87
  • 167
Brigand
  • 84,529
  • 20
  • 165
  • 173
  • 5
    Considering that React uses latest ES + Babel (for the most part), this should be the accepted answer. The Object spread operator is the most concise and easy to grep. – ortonomy Jul 24 '17 at 07:35
  • Apparently this method doesnt work on Safari. It behaves differently. – tnkh Jan 29 '19 at 09:38
  • @Brigand Can you please elaborate more to clarify (for maybe beginners). Just getting back into JS and react, and this is still a bit confusing (specifically the difference between in functions in the first code block). Thanks :) – chris Frisina Feb 16 '20 at 20:35
16

let newAccess = Object.assign({}, this.state.access);
newAccess.hospital_id = 1;
this.setState({access: newAccess});
J. Mark Stevens
  • 4,911
  • 2
  • 13
  • 18
  • 11
    you should not mutate state directly – Yaron Levi Feb 04 '17 at 22:21
  • 12
    [Do Not Modify State Directly](https://facebook.github.io/react/docs/state-and-lifecycle.html#do-not-modify-state-directly) – lithiumsheep Mar 23 '17 at 21:22
  • 3
    Using setState is the suggested method for modifying the state indirectly. Now it may be that you mean let newAccess = Object.assign({}, this.state.access); should be used which did not occur to me before. In which case I would probably agree since that is the pattern I use elsewhere. – J. Mark Stevens Mar 24 '17 at 22:29
2

My preferred way of doing this now is as simple as:

let newAccess = this.state.access;
newAccess.hospital_id = 1;
setState({access: newAccess});

Slightly simpler than the current accepted answer.

EDIT (based on the question from @SILENT )

It looks like this is actually a potentially dangerous method. Further reading here React: A (very brief) talk about immutability.

Looks like a better way to do this would be:

let newAccess = Object.assign({}, this.state.access, {hospital_id:1});
this.setState({access: newAccess});
Dan G Nelson
  • 862
  • 2
  • 11
  • 30
1

Another way to do this would be

const newAccess = {...this.state.access};
newAccess.hospital_id = 1;
setState({access: newAccess});

Use of the spread operator creates a clone of this.state.access.

twocents
  • 333
  • 1
  • 2
  • 13
1

I had this same issue too(not the same context) anyways, i did the code below in my own work and it worked perfectly

this.setState({
  access: {
    ...this.state.access,
    hospital_id: 1,
  },
});
Shay1309
  • 19
  • 2
0

The best way for this , is ES6 , you can update state with spread object ( in this way you re assign the "hospital_id" ), like this :

this.setState({ access : {...this.state.access , hospital_id : 1 } })