I have created a BMI calculator app and I want it to change one of the 6 buttons from "btn-primary" to "btn-danger" colors depending on the BMI answer. but after calculation, no button changes color but the answer changes immediately.
Here is CalculationForm.js Component
import React, { useState } from "react";
import "../stylesheets/calculation-form.css";
import Answer from "./Answer";
export default function CalculationForm() {
const [weightVal, setWeightVal] = useState(0);
const [heightVal, setHeightVal] = useState(0);
const handleHeight = (e) => {
let input = e.target.value;
if (input.match(/^([0-9]{1,})?(.)?([0-9]{1,})?$/)) {
setHeightVal(input);
}
};
const handleWeight = (e) => {
let input = e.target.value;
if (input.match(/^([0-9]{1,})?(.)?([0-9]{1,})?$/)) {
setWeightVal(input);
}
};
let content;
content = (
<div className="container d-flex flex-column align-items-center justify-content-center">
<form className="form">
<div>
<label>
Weight (Kg):
<input
type=""
value={weightVal}
onChange={handleWeight}
name="weight"
/>
</label>
<label>
Height (M):
<input
type="text"
value={heightVal}
onChange={handleHeight}
name="height"
/>
</label>
</div>
</form>
<Answer weightVal={weightVal} heightVal={heightVal} />
</div>
);
return <div className="container">{content}</div>;
}
Here is Answer.js component
import React, { useEffect, useState } from "react";
import "../stylesheets/answer.css";
export default function Answer(props) {
const [buttons, setButtons] = useState([
"btn-primary",
"btn-primary",
"btn-primary",
"btn-primary",
"btn-primary",
"btn-primary",
]);
let height = parseFloat(props.heightVal);
let weight = parseFloat(props.weightVal);
let answer = weight / Math.pow(height, 2);
useEffect((answer) => {
let newButtons = [
"btn-primary",
"btn-primary",
"btn-primary",
"btn-primary",
"btn-primary",
"btn-primary",
];
if (answer < 18.5) {
newButtons[0] = "btn-danger";
} else if (answer >= 18.5 && answer < 25) {
newButtons[1] = "btn-danger";
} else if (answer >= 25 && answer < 30) {
newButtons[2] = "btn-danger";
} else if (answer >= 30 && answer < 35) {
newButtons[3] = "btn-danger";
} else if (answer >= 35 && answer < 40) {
newButtons[4] = "btn-danger";
} else if (answer >= 40) {
newButtons[5] = "btn-danger";
}
setButtons([...newButtons]);
});
return (
<div className="container d-flex flex-column align-items-center justify-content-center text-center">
<p className="align-self-center h1 m-5">
Your BMI is:{" "}
{isNaN(answer) || !isFinite(answer) ? "Not measured" : answer}
</p>
<div className="btn-group d-flex flex-row">
<button className={"btn " + buttons[0]}> Underweight</button>
<button className={"btn " + buttons[1]}> Normal</button>
<button className={"btn " + buttons[2]}> Overweight</button>
<button className={"btn " + buttons[3]}>Class I obesity</button>
<button className={"btn " + buttons[4]}>Class II obesity</button>
<button className={"btn " + buttons[5]}>Class III obesity</button>
</div>
</div>
);
}
I tried to pass buttons from CalculationForm.js component but I didn’t find it helpful.
2
Answers
Added a reliance array to the useEffect hook that includes the answer variable. This ensures that the effect only runs when the answer variable changes.
You don’t need a
useState
anduseEffect
here. Create an array of static buttons, with their threshold values (t
). Find the index of the 1st button that it’st
value is greater thenanswer
. Render the buttons usingArray.map()
, and set thebtn-danger
class to the button that matches thedangerIndex
: