[reactjs] Updating an object with setState in React

Is it at all possible to update object's properties with setState?

Something like:

this.state = {
   jasper: { name: 'jasper', age: 28 },
}

I have tried:

this.setState({jasper.name: 'someOtherName'});

and this:

this.setState({jasper: {name: 'someothername'}})

The first results in a syntax error and the second just does nothing. Any ideas?

This question is related to reactjs state

The answer is


You can try with this:

this.setState(prevState => {
   prevState = JSON.parse(JSON.stringify(this.state.jasper));
   prevState.name = 'someOtherName';
   return {jasper: prevState}
})

or for other property:

this.setState(prevState => {
   prevState = JSON.parse(JSON.stringify(this.state.jasper));
   prevState.age = 'someOtherAge';
   return {jasper: prevState}
})

Or you can use handleChage function:

handleChage(event) {
   const {name, value} = event.target;
    this.setState(prevState => {
       prevState = JSON.parse(JSON.stringify(this.state.jasper));
       prevState[name] = value;
       return {jasper: prevState}
    })
}

and HTML code:

<input 
   type={"text"} 
   name={"name"} 
   value={this.state.jasper.name} 
   onChange={this.handleChange}
/>
<br/>
<input 
   type={"text"} 
   name={"age"} 
   value={this.state.jasper.age} 
   onChange={this.handleChange}
/>

Use spread operator and some ES6 here

this.setState({
    jasper: {
          ...this.state.jasper,
          name: 'something'
    }
})

Also, following Alberto Piras solution, if you don't want to copy all the "state" object:

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

    let jasperCopy = Object.assign({}, this.state.jasper);
    jasperCopy[inputName].name = inputValue;

    this.setState({jasper: jasperCopy});
  }

try this,it should work fine

this.setState(Object.assign(this.state.jasper,{name:'someOtherName'}));

The first case is indeed a syntax error.

Since I can't see the rest of your component, it's hard to see why you're nesting objects in your state here. It's not a good idea to nest objects in component state. Try setting your initial state to be:

this.state = {
  name: 'jasper',
  age: 28
}

That way, if you want to update the name, you can just call:

this.setState({
  name: 'Sean'
});

Will that achieve what you're aiming for?

For larger, more complex data stores, I would use something like Redux. But that's much more advanced.

The general rule with component state is to use it only to manage UI state of the component (e.g. active, timers, etc.)

Check out these references:


Your second approach doesn't work because {name: 'someothername'} equals {name: 'someothername', age: undefined}, so theundefined would overwrite original age value.

When it comes to change state in nested objects, a good approach would be Immutable.js

this.state = {
  jasper: Record({name: 'jasper', age: 28})
}

const {jasper} = this.state
this.setState({jasper: jasper.set(name, 'someothername')})

You can try with this: (Note: name of input tag === field of object)

<input name="myField" type="text" 
      value={this.state.myObject.myField} 
     onChange={this.handleChangeInpForm}>
</input>

-----------------------------------------------------------
handleChangeInpForm = (e) => {
   let newObject = this.state.myObject;
   newObject[e.target.name] = e.target.value;
   this.setState({
     myObject: newObject 
   })
}

this is another solution using immer immutabe utility, very suited for deeply nested objects with ease, and you should not care about mutation

this.setState(
    produce(draft => {
       draft.jasper.name = 'someothername'
    })
)

Try with this:

const { jasper } = this.state; //Gets the object from state
jasper.name = 'A new name'; //do whatever you want with the object
this.setState({jasper}); //Replace the object in state

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. Make sure to use the same name used into state object (in this case 'friendName')

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

This setup worked for me:

let newState = this.state.jasper;
newState.name = 'someOtherName';

this.setState({newState: newState});

console.log(this.state.jasper.name); //someOtherName

Another option: define your variable out of the Jasper object and then just call a variable.

Spread operator: ES6

this.state = {  jasper: { name: 'jasper', age: 28 } } 

let foo = "something that needs to be saved into state" 

this.setState(prevState => ({
    jasper: {
        ...jasper.entity,
        foo
    }
})

This is the fastest and the most readable way:

this.setState({...this.state.jasper, name: 'someothername'});

Even if this.state.jasper already contains a name property, the new name name: 'someothername' with be used.


I know there are a lot of answers here, but I'm surprised none of them create a copy of the new object outside of setState, and then simply setState({newObject}). Clean, concise and reliable. So in this case:

_x000D_
_x000D_
const jasper = { ...this.state.jasper, name: 'someothername' }_x000D_
this.setState(() => ({ jasper }))
_x000D_
_x000D_
_x000D_

Or for a dynamic property (very useful for forms)

_x000D_
_x000D_
const jasper = { ...this.state.jasper, [VarRepresentingPropertyName]: 'new value' }_x000D_
this.setState(() => ({ jasper }))
_x000D_
_x000D_
_x000D_


Simple and dynamic way.

This will do the job, but you need to set all the ids to the parent so the parent will point to the name of the object, being id = "jasper" and name the name of the input element = property inside of the object jasper.

handleChangeObj = ({target: { id , name , value}}) => this.setState({ [id]: { ...this.state[id] , [name]: value } });

Without using Async and Await Use this...

funCall(){    
     this.setState({...this.state.jasper, name: 'someothername'});
}

If you using with Async And Await use this...

async funCall(){
      await this.setState({...this.state.jasper, name: 'someothername'});
}

Examples related to reactjs

Error: Node Sass version 5.0.0 is incompatible with ^4.0.0 TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received type undefined raised when starting react app Template not provided using create-react-app How to resolve the error on 'react-native start' Element implicitly has an 'any' type because expression of type 'string' can't be used to index Invalid hook call. Hooks can only be called inside of the body of a function component How to style components using makeStyles and still have lifecycle methods in Material UI? React Hook "useState" is called in function "app" which is neither a React function component or a custom React Hook function How to fix missing dependency warning when using useEffect React Hook? Unable to load script.Make sure you are either running a Metro server or that your bundle 'index.android.bundle' is packaged correctly for release

Examples related to state

How to Set/Update State of StatefulWidget from other StatefulWidget in Flutter? Updating an object with setState in React Why can't I change my input value in React even with the onChange listener React setState not updating state How to use onClick with divs in React.js React - how to pass state to another component React.js, wait for setState to finish before triggering a function? setInterval in a React app React: how to update state.item[1] in state using setState? AngularJS ui router passing data between states without URL