React中useEffect未更新按钮数组:BMI计算器按钮样式不切换
BMI计算器按钮样式不更新问题修复方案
问题根源
你的代码存在两个关键问题导致按钮样式无法随BMI值更新:
- useEffect使用错误:
- 给useEffect的回调函数添加了
(answer)参数,这会覆盖组件内计算的BMI值,导致逻辑判断用的是无效变量 - 未设置依赖数组,要么useEffect只在初始渲染时执行一次,要么因为
setButtons触发重渲染而陷入无限循环,无法响应BMI值变化
- 给useEffect的回调函数添加了
- 未处理BMI无效情况:当身高为0时,计算出的
answer会变成Infinity,此时样式逻辑会错误触发最后一个按钮的样式
修复方案(基于原有代码修改)
修改Answer.js中的useEffect部分,调整如下:
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写法 useEffect(() => { let newButtons = [ "btn-primary", "btn-primary", "btn-primary", "btn-primary", "btn-primary", "btn-primary", ]; // 先判断BMI值是否有效 if (!isNaN(answer) && isFinite(answer)) { 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]); // 添加依赖数组,仅当answer变化时执行 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.toFixed(2)} </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> ); }
更简洁的优化方案(无需维护buttons状态)
可以直接根据BMI值动态计算每个按钮的类名,省去额外的状态管理:
import React from "react"; import "../stylesheets/answer.css"; export default function Answer(props) { let height = parseFloat(props.heightVal); let weight = parseFloat(props.weightVal); let answer = weight / Math.pow(height, 2); const isValidBMI = !isNaN(answer) && isFinite(answer); // 定义每个按钮对应的BMI范围 const buttonConfigs = [ { label: "Underweight", range: val => val < 18.5 }, { label: "Normal", range: val => val >=18.5 && val <25 }, { label: "Overweight", range: val => val >=25 && val <30 }, { label: "Class I obesity", range: val => val >=30 && val <35 }, { label: "Class II obesity", range: val => val >=35 && val <40 }, { label: "Class III obesity", range: val => val >=40 }, ]; 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:{" "} {isValidBMI ? answer.toFixed(2) : "Not measured"} </p> <div className="btn-group d-flex flex-row"> {buttonConfigs.map((config, index) => ( <button key={index} className={`btn ${isValidBMI && config.range(answer) ? "btn-danger" : "btn-primary"}`} > {config.label} </button> ))} </div> </div> ); }
内容的提问来源于stack exchange,提问作者Amirreza
相关产品推荐
相关产品推荐

