-2

When I have a button class called "DrawButton", that has this render

render() {
    return(
        <Button
            onClick={this.props.toggleDraw.bind(this)}
            style={{
                backgroundColor: this.props.drawMode ? 'red' : 'blue'
            }}
        >
            Draw
        </Button>
    );
}

And in parent App.js the state gets defined

state = {
        drawMode: false
}

and there is a handler function

toggleDraw = (e) => {
    console.log('App.js drawMode:' + this.state.drawMode);
    this.setState({
        drawMode: !this.state.drawMode
    });
    console.log('App.js drawMode:' + this.state.drawMode);
}

And finally the button:

render() {
  return (
    <div className="App">
        <DrawButton 
            toggleDraw={this.toggleDraw} 
            drawMode={this.state.drawMode}
        />
    </div>
  );
}

The state doesn't get updated properly. It outputs the following:

First click on the Button

App.js drawMode:false
App.js:27
App.js drawMode:false
App.js:31

Before the setState ran, the drawMode is false after setState ran, the drawMode is still false.

But the button still gets a red background.

Second click on the Button:

App.js drawMode:true
App.js:22
App.js drawMode:true
App.js:26

But the button is blue again despise drawMode in state is set to true.

Why is this inconsistency happening?

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • 2
    `setState` is asynchronous, so it won't be changed on next line of code when you log it – Jayce444 Oct 28 '19 at 01:36
  • 1
    You've got your `.bind(this)` in the wrong place, it needs to be in the parent not the child. – david Oct 28 '19 at 01:36
  • How can I fix that it remains consistent? –  Oct 28 '19 at 01:36
  • You need to use `await` in the `async` function to get the right result. – wisn Oct 28 '19 at 01:37
  • 1
    @WisnuAdiNurcahyo What? there is no async function here. – david Oct 28 '19 at 01:38
  • @david That doesn't change the result –  Oct 28 '19 at 01:38
  • 1
    @bxyify Then you're not showing us all the code. With your code and my fix it works fine (bar the console log thing other people are talkign about) https://jsfiddle.net/dh7mkzwq/ – david Oct 28 '19 at 01:43
  • @david Then you must have a different browser than me (Firefox) because your jsfiddle gives the exact same result when I click on the button (it gets red): App.js drawMode:false show:27:5 App.js drawMode:false show:31:5 –  Oct 28 '19 at 01:48
  • Possible duplicate of [React setState not updating state](https://stackoverflow.com/questions/41446560/react-setstate-not-updating-state) – Nick is tired Oct 28 '19 at 02:34

1 Answers1

1

Firstly, your bind was used incorrectly, in your DrawButton onClick handler, just call this.props.toggleDraw. This code : this.props.toggleDraw.bind(this) should be in the constructor of App.js file.

Secondly, do not use the console.log to check the value of state after setting, because the setState function runs asynchronously, use setState callback to check the value after setting:

toggleDraw = (e) => {
    console.log('App.js drawMode:' + this.state.drawMode);
    this.setState(
        { drawMode: !this.state.drawMode },
        () => console.log('App.js drawMode:' + this.state.drawMode)
    ),
}
thelonglqd
  • 1,805
  • 16
  • 28
  • You are right, this fixes the console log problem. But now the state doesn't get passed correctly to all components. I just added another component and while button gets the updated state of drawMode, the other component defined before doesn't get it at the same time. I have to toggle it twice until it gets populated to the other component. I will edit the question accordingly. –  Oct 28 '19 at 01:54
  • could you add the code of "another" component, I will take a look on that. – thelonglqd Oct 28 '19 at 01:56
  • 2
    Maybe I should create another question with that because that changes the question too much... –  Oct 28 '19 at 02:00