React状态更新异常:isCandidate/isCompany存入数据库为null求助
问题原因分析与修复方案
核心问题1:body 对象捕获了初始状态值
你的 body 对象是在组件顶层定义的,只会在组件首次挂载时创建一次,此时 isCandidate 和 isCompany 的值都是初始的 null。即使在 onSubmitHandler 中调用了状态更新函数,body 对象也不会自动更新,仍然会使用最初的 null 值发送请求。
核心问题2:React 状态更新是异步的
setIsCandidate 和 setIsCompany 是异步操作,调用后不会立即更新状态值。所以即使你在更新状态后立刻打印 body,它依然是旧的状态值。
额外问题:重复触发提交逻辑
你的表单 onSubmit 和按钮 onClick 都绑定了 onSubmitHandler,点击按钮时会同时触发这两个事件,导致两次 API 请求。
修复方案
方案1:直接根据 userRole 计算值(推荐)
既然 isCandidate 和 isCompany 的值完全由 userRole 决定,不需要维护独立的状态变量,直接在提交时计算即可:
console.log("signin card"); const [phoneNumber, setPhoneNumber] = useState(""); // 移除不必要的状态变量 // const [isCompany, setIsCompany] = useState(null); // const [isCandidate, setIsCandidate] = useState(null); const onSubmitHandler = async () => { // 直接根据 userRole 计算两个字段的值 const isCandidate = userRole === "candidate"; const isCompany = userRole === "company"; // 在提交时创建 body 对象 const body = { phoneNumber, isCandidate, isCompany, }; console.log("body : ", body); const res = await axios.post( "http://localhost:8000/user-role/recorded", body ); const otpRes = await axios.get( `https://2factor.in/API/V1/b3014f3e-2e06-11ea-9fa5-0200cd936042/SMS/+91${phoneNumber}/AUTOGEN3` ); console.log("res = ", res); console.log("otp = ", otpRes); }; const onChangeHandler = (e) => { setPhoneNumber(e.target.value); }; return ( <> <div className="modal" id="signInCard" tabIndex="-1" aria-hidden="true" style={{ position: "fixed", top: "0%", left: "50%", width: "100%", height: "100%", overflowX: "hidden", overflowY: "auto", transform: "translateX(-50%)", zIndex: "1055", }} > <div className="modal-dialog modal-dialog-centered" style={{ width: "20%" }} > <div className="modal-content"> <div className="modal-body p-5"> <div className="position-absolute end-0 top-0 p-3"> <button type="button" className="btn-close" data-bs-dismiss="modal" aria-label="Close" ></button> </div> <div className="auth-content"> <div className="w-100"> <div className="text-center mb-4"> <h5>Sign In as {userRole}</h5> <p className="text-muted"></p> </div> <form onSubmit={(e) => { e.preventDefault(); // 阻止表单默认提交行为 onSubmitHandler(); }} className="auth-form" > <div className="input-group mb-3"> <div className="input-group-prepend"> <span className="input-group-text" id="basic-addon1"> +91 </span> </div> <input type="number" name="phoneNumber" className="form-control" id="usernameInput" placeholder="Enter your phone no." aria-label="number" aria-describedby="basic-addon1" onChange={onChangeHandler} value={phoneNumber} /> <span className="text-danger"></span> </div> <div className="text-center"> <a href="#otpModal" data-bs-toggle="modal"> <button type="submit" className="btn btn-primary w-100" // 移除重复的 onClick 绑定 // onClick={onSubmitHandler} > Sign In </button> </a> </div> </form> </div> </div> </div> </div> </div> </div> </> );
方案2:若需保留状态变量
如果你的组件其他地方需要用到 isCandidate 和 isCompany 的状态,可以先计算值,再同步更新状态和请求体:
console.log("signin card"); const [phoneNumber, setPhoneNumber] = useState(""); const [isCompany, setIsCompany] = useState(null); const [isCandidate, setIsCandidate] = useState(null); const onSubmitHandler = async () => { const isCandidateVal = userRole === "candidate"; const isCompanyVal = userRole === "company"; // 更新状态(供组件其他部分使用) setIsCandidate(isCandidateVal); setIsCompany(isCompanyVal); // 使用计算出的值创建请求体 const body = { phoneNumber, isCandidate: isCandidateVal, isCompany: isCompanyVal, }; console.log("body : ", body); const res = await axios.post( "http://localhost:8000/user-role/recorded", body ); const otpRes = await axios.get( `https://2factor.in/API/V1/b3014f3e-2e06-11ea-9fa5-0200cd936042/SMS/+91${phoneNumber}/AUTOGEN3` ); console.log("res = ", res); console.log("otp = ", otpRes); }; // 其余代码同方案1...
关键修复点总结
- 移除组件顶层的
body对象,在提交时动态创建,确保使用最新值。 - 避免依赖异步状态更新后的结果直接构建请求体,直接从
userRole计算值更可靠。 - 移除按钮上的
onClick绑定,只保留表单的onSubmit,并添加e.preventDefault()阻止默认提交行为。
内容的提问来源于stack exchange,提问作者Lord
相关产品推荐
相关产品推荐

