skip to Main Content

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): &nbsp;
            <input
              type=""
              value={weightVal}
              onChange={handleWeight}
              name="weight"
            />
          </label>
          <label>
            Height (M): &nbsp;&nbsp;
            <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


  1. useEffect(() => {
        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]);
      }, [answer]);
    

    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.

    Login or Signup to reply.
  2. You don’t need a useState and useEffect here. Create an array of static buttons, with their threshold values (t). Find the index of the 1st button that it’s t value is greater then answer. Render the buttons using Array.map(), and set the btn-danger class to the button that matches the dangerIndex:

    const buttons = [
      { t: 18.5, txt: 'Underweight' },
      { t: 25, txt: 'Normal' },
      { t: 30, txt: 'Overweight' },
      { t: 35, txt: 'Class I obesity' },
      { t: 40, txt: 'Class II obesity' },
      { t: Infinity, txt: 'Class III obesity' },
    ]
    
    function Answer({ heightVal, weightVal }) {
      const height = parseFloat(heightVal);
      const weight = parseFloat(weightVal);
      const answer = weight / Math.pow(height, 2);
      
      const dangerIndex = buttons.findIndex(o => answer < o.t);
    
      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">
            {buttons.map(({ txt }, i) => (
              <button key={txt}
                className={`btn ${i === dangerIndex ? 'btn-danger' : ''}`}>
                {txt}
              </button>
            ))}
          </div>
        </div>
      );
    }
    
    ReactDOM
      .createRoot(root)
      .render(<Answer heightVal={1.6} weightVal={65} />)
    .btn-danger {
      color: red;
    }
    <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
    
    <div id="root"></div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search