简体   繁体   中英

ReactJS - how do I update nested and “normal” state properties?

This is how my state looks like:

constructor(props, context) {
    super(props, context);

    this.state = {
      show: false,
      btnLabel: 'GO!',
      car: {
        owner: false, 
        manufacturer: false, 
        color: false
      }
    };
  }

and this is how I modify state :

handleClickFetchPrice() {
      this.setState({btnLabel: 'Fetching data...' });
      console.log(this.state.fetchPriceBtn);

      const url = 'some url';
      axios.get(url)
        .then(res => {
          let car = [...this.state.car];
          car.owner = res.data.owner;
          car.manufacturer = res.data.manufacturer;
          car.color = res.data.color;
          this.setState({car});
        })
  }

The attribute car is updated, but fetchPriceBtn is not - the output of console.log(this.state.fetchPriceBtn); is still GO! .

What am I overlooking? Why the fetchPriceBtn is not updated?

React setState is an asynchronous process - you don't know exactly when it will be updated, you can only schedule the update.

To achieve your desired functionality, you can provide a callback into the setState method.

this.setState({ btnLabel: 'Fetching data...' }, () => console.log(this.state.fetchPriceBtn))

You can learn more following the documentation on the method.

@christopher is right, setState is an asynchronous process. But when second time call handleClickFetchPrice() function your btnLabel is value will be equal to Fetching data...

As answered in previous answers setState is asynchronous, so your console.log can't catch up the state change immediately. Again as suggested you can use callback function to track this change but if you use console.log just for debugging or want to see what changes in your state you can do this in your render function. And using a callback just for debug is not a nice way. Its purpose somehow different and if you check the official documentation, componentDidMount method is being suggested for such logic.

render() {
    console.log( this.state.foo );
    return (...)
}

If you do that you see two console.log output, one before state change and one after.

Also, your state operations might be enhanced. You car property is not an array, but you are converting it to an array and setting it? Is this what you intend:

axios.get(url)
    .then(res => {
        const { owner, manufacturer, color } = res.data;
        this.setState( prevState => ( { car: { ...prevState.car, owner, manufacturer, color } } ) );
     })

Here we are not mutating our state directly, instead we are using spread operator and setting the desired properties. For your example we are setting the whole property actually.

One last note, I think you want to do that something like that:

this.setState( { btnLabel: "fetching } );
axios.get(url)
    .then(res => {
        const { owner, manufacturer, color } = res.data;
        this.setState( prevState => ( { car: { ...prevState.car, owner, manufacturer, color }, btnLabel: "go" } ) );
     })

If your intention is somehow to do a status change/check this might no be a good logic as you have seen setState is not synchronous. Do this carefully.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM