React: Inconsistent State after setState in ComponentDidMount

Created on 5 Oct 2016  路  6Comments  路  Source: facebook/react

I'm working with a library which needs to be initialized in componentDidMount according to their documentation but I am not getting the expected results when I update the state, like so:

class ChartContents extends React.Component {

  constructor() {
  super();
  this.state = { stxx: {} };
}
  componentDidMount() {
    let stxx=new STXChart(this.chartContainer);
    stxx.newChart("SPY", sampleData);
    this.setState({stxx});
    console.log(this);
    console.log(this.state);
}
  render()
   {
     const Style = {
       height:'600px',
       position:'relative',
       width:'800px'
     }
    return (
      <div id="quickStart" className="chartContainer" style={Style}  ref={(c)=>this.chartContainer=c}></div>
    );
  }
}

When I view the object in the debugger I find my state was updated on the object ChartContents with the newly created chart but when I try to access it, it returns the empty object it was initialized with. I did also see the eslint warning and I was wondering how to solve this problem. Thanks!

console

Unconfirmed

Most helpful comment

the console.log statements are currently in the componentDidMount function

There's no guarantee that state updates will complete immediately, since they're batched. See the docs on setState

setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value.
There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.

Logging state immediately after calling setState will likely give you the previous state since it hasn't actually updated yet. If you want to perform a side effect or something after the state has been updated, you can provide a callback to setState

this.setState(newState, () => {
  // this callback will be called once state is updated
}

however the result is the same in the render function, I can find the state updated on this (the class object) but not this.state

Are you saying that this.state does not contain the updated state when render is called after you setState in componentDidMount?

All 6 comments

setState is not guaranteed to update state immediately, state updates are batched. When/where are you accessing state in both cases?

@Aweary the console.log statements are currently in the componentDidMount function, however the result is the same in the render function, I can find the state updated on this (the class object) but not this.state

the console.log statements are currently in the componentDidMount function

There's no guarantee that state updates will complete immediately, since they're batched. See the docs on setState

setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value.
There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.

Logging state immediately after calling setState will likely give you the previous state since it hasn't actually updated yet. If you want to perform a side effect or something after the state has been updated, you can provide a callback to setState

this.setState(newState, () => {
  // this callback will be called once state is updated
}

however the result is the same in the render function, I can find the state updated on this (the class object) but not this.state

Are you saying that this.state does not contain the updated state when render is called after you setState in componentDidMount?

@Aweary You are correct, I did read the docs on setState and saw state is not guaranteed to be updated right away but when I saw only the state on this was updated with the new state but not this.state within the same function in the program I thought this strange behavior and I was doing something wrong. Also when I said:

however the result is the same in the render function, I can find the state updated on this (the class object) but not this.state

I should have been more specific, the first render give me the same output, which is a unexpected as it is called before componentDidMount() and I was showing the STXChart on the state of this but not this.state but after the second render both this and this.state contain the new chart object and not the empty object it was initialized with. I expected them both to be updated at the same time, the inconsistency is what made me question it.

Here is a more verbose output:

class ChartContents extends React.Component {

  constructor() {
  super();
  this.state = { stxx: {} };
}
  componentDidMount() {
    let stxx=new STXChart(this.chartContainer);
    stxx.newChart("SPY", sampleData);
    this.setState({stxx});

    console.log('DID MOUNT');
    console.log(this);
    console.log(this.state);

}
  render()
   {
     const Style = {
       height:'600px',
       position:'relative',
       width:'800px'
     }

     console.log('RENDER');
     console.log(this);
     console.log(this.state);

    return (
      <div id="quickStart" className="chartContainer" style={Style}  ref={(c)=>this.chartContainer=c}></div>
    );
  }
}

console

The red arrows show where it's inconsistent and the green arrow shows where both the state on this and this.state match. Again, the inconsistency is what concerned me, I'm just trying to understand if this is because of something I am doing wrong or if this is expected behavior.

@AvaKathrynShaw When you log in Chrome, it's not creating a copy of the object. It actually reads the data from the object when you expand it in the console. Hence, when you expand the object in the console in this case, you're seeing the value it has now, not the value it had when you logged it.

@syranide Thank you! I should have thought to look into how the browser worked as well, I'm new to React and assumed I did something wrong. Will close issue.

Was this page helpful?
0 / 5 - 0 ratings