You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 01:15:46