cphill
cphill

Reputation: 5914

State Boolean Value Not Correct After OnChange is Called

I am trying to flip a state's boolean value that is associated with a checkbox input each time the checkbox is clicked, but for some reason I am getting two different values at different points in the act of checking or unchecking the checkbox.

They both start out in the true state that is set in the constructor on component load, but then on the first uncheck of the input, the state correctly switches to false in render(), but in my drawChart() function it still recognizes the true state. When I recheck the input the render() correctly switches to true while the drawChart() function is now false. Is there something wrong with my function call?

For additional context, the drawChart() is used to trigger a D3.js chart and has an if/else statement that checks the state to see if a bar chart should appear or should be removed.

Here is the relevant code:

import React, { Component } from 'react';

class SleepQualityBarLineGraph extends Component {
    constructor(props) {
        super(props);
        this.container = React.createRef();

        this.awakeChange = this.awakeChange.bind(this);

        this.state = {
            awakeBox: true
        };
    }

    awakeChange(event) {
        this.setState({awakeBox: !this.state.awakeBox});
        this.removePreviousChart();
        this.drawChart(this.props.data.count_sleep_disruptions)
    }

    shouldComponentUpdate(nextProps) {
        if(nextProps.data == this.props.data) {
            return true;
        } else {
            this.removePreviousChart();
            this.drawChart(nextProps.data.count_sleep_disruptions);
            return false;
        }
    }

    removePreviousChart() {
        const chart = document.getElementById('bar-line-time-series-sleep-quality');
        while(chart.hasChildNodes()){
          chart.removeChild(chart.lastChild);
        }
    }

    drawChart(dataObj) {
        if(this.state.awakeBox){
            console.log("Checked in drawChart()")
            console.log(this.state.awakeBox)
        } else {
            console.log("Not Checked in drawChart()")
            console.log(this.state.awakeBox)
        }
    }

    render() {
        if(this.state.awakeBox){
            console.log("checked! in render")
            console.log(this.state.awakeBox)
        } else {
            console.log("not checked! in render")
            console.log(this.state.awakeBox)
        }
        return (
            <div className="grid grid-flow-col grid-cols-3 mb-5">
                <div className="bg-green-600 p-2 rounded-md mr-5">
                    <input type="checkbox" class="form-checkbox text-green-800" id="awakeBox" defaultChecked={this.state.awakeBox}
              onChange={this.awakeChange} />
                    <span className="ml-2 text-green-300">Awake</span>
                </div>
                <div className="grid-cols-1 mx-auto max-w-3xl bg-green-200">
                    <div ref={this.container} id="bar-line-time-series-sleep-quality"></div>
                </div>
            </div>
        )
    }

}

export default SleepQualityBarLineGraph;

Upvotes: 0

Views: 403

Answers (1)

Chanandrei
Chanandrei

Reputation: 2421

its because setState is asynchronous. Your drawChart() function will execute first before setState take effect. setState has a second parameter which is callback after the state successfully changed

awakeChange(event) {
    this.setState({awakeBox: !this.state.awakeBox} , function(){
       this.removePreviousChart();
       this.drawChart(this.props.data.count_sleep_disruptions)
    });
}

see also this question for reference.

Upvotes: 1

Related Questions