Francisco
Francisco

Reputation: 567

React update issue

I have a page where I update two fields and successfully get the state variables updated, to a point. The last update reflects in the render part, but can not pass beyond that. What am I doing wrong? Am I missing something? Suggestions?

When typing to the height field the corresponding state is updated, same goes for the weight field, and rendering this is ok. But when I try to make the calculation, I can not do so accurately since I am missing the last update on any one of the mentioned fields.

// index.jsx
import React from "react";
import ReactDOM from "react-dom";

class BmiReactForm extends React.Component {

    constructor(props) {

        super(props);
        this.state = { 
            height: 0,
            weight: 0,
            factor: 'Unknown',
            category: 'Undefined'
        };

        this.handleChange = this.handleChange.bind(this);
    }

    handleChange(input, value) {

        this.setState({[input]: value});

        const height = parseFloat(this.state.height).toFixed(2);
        const weight = parseFloat(this.state.weight).toFixed(2);
        this.bmiCalc(height, weight);
    }

    bmiCalc(height, weight) {

        if ((height > 0) && (weight > 0)) {

            let bmiNumber = weight/(height * height);

            let bmiString = 'Undefined'
            switch(true) {
                case (bmiNumber < 15):
                    bmiString = 'Very severely underweight';
                    break;
                case (bmiNumber >= 15 && bmiNumber < 16):
                    bmiString = 'Severely underweight';
                    break;
                case (bmiNumber >= 16 && bmiNumber < 18.5):
                    bmiString = 'Underweight';
                    break;
                case (bmiNumber >= 18.5 && bmiNumber < 25):
                    bmiString = 'Normal (healthy weight)';
                    break;
                case (bmiNumber >= 25 && bmiNumber < 30):
                    bmiString = 'Overweight';
                    break;
                case (bmiNumber >= 30 && bmiNumber < 35):
                    bmiString = 'Obese Class I (Moderately obese)';
                    break;
                case (bmiNumber >= 35 && bmiNumber < 40):
                    bmiString = 'Obese Class II (Severely obese)';
                    break;
                case (bmiNumber >= 40):
                    bmiString = 'Obese Class III (Very severely obese)';
                    break;
            }

            this.setState({factor: bmiNumber});
            this.setState({category: bmiString});

        } else {

            this.setState({factor: 'Unknown'});
            this.setState({category: 'Undefined'});
        }
    }

    render() {

        const factor = this.state.factor;
        const category = this.state.category;

        return (
            <div>
                <form>
                    <input id="csrf_token" name="csrf_token" type="hidden" value="IjFhOWU1NTg2OWFiYTA0NTMzYjhhMDM5YWRhOGNjOThhMTk1MDJiNjEi.DYtlyQ.k8gVqs8uX9TLTOrnTF3aeOJXjoY" />
                    <p>
                        <label>Height</label> <input class="form-control"
                            id="height" type="text"
                            name="height"
                            placeholder={this.state.height}
                            onChange={e => this.handleChange('height', e.target.value)} />
                    </p>
                    <p>
                        <label>Weight</label> <input class="form-control"
                            id="weight" type="text"
                            name="weight"
                            placeholder={this.state.weight}
                            onChange={e => this.handleChange('weight', e.target.value)} />
                    </p>
                    <div class="jumbotron">
                        <p>
                            <label>Factor:</label> <span>{factor}</span>
                        </p>
                        <p>
                            <label>Category:</label> <span>{category}</span>
                        </p>
                    </div>
                </form>
            </div>
        );
    }
}

ReactDOM.render(<BmiReactForm />, document.getElementById("BmiReactForm"));

What is needed to guarantee the calculation will always take into account the latest update?

Thanks for your time and effort.

Upvotes: 0

Views: 48

Answers (2)

Pinki Gupta
Pinki Gupta

Reputation: 18

Use this.setState( ( prevState, props ) => ({}) ) method at the place of this.setState({}). Because it's not ensuring that the you are getting a new updated state every time.

Upvotes: 0

Joshua Underwood
Joshua Underwood

Reputation: 927

call this.bmiCalc in the callback to setState

e.g.

this.setState({[ input ]}: value }, () => { 
    const height = parseFloat(this.state.height).toFixed(2);
    const weight = parseFloat(this.state.weight).toFixed(2);
    this.bmiCalc(height, weight);
});

This ensures that the state transition occurs BEFORE you attempt a calculation.

If you're not using ES6 (you are, but just in case anyway)

    this.setState({[ input ]}: value }, function() { 
    const height = parseFloat(this.state.height).toFixed(2);
    const weight = parseFloat(this.state.weight).toFixed(2);
    this.bmiCalc(height, weight);
});

Upvotes: 1

Related Questions