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

react-hook-form的onSubmit函数未触发,Controller管理Input异常求助

问题原因
  • 你没有把price状态的变更同步给React Hook Form(以下简称RHF)管理的表单字段,导致voucherPrice字段一直停留在初始的false值,不符合你定义的yup校验规则(正数字、必填),表单校验失败直接拦截了提交,所以onSubmit不会触发。
  • 具体存在两处同步遗漏:
    1. 手动修改Input值时,你只调用了自定义的handlePriceInputChange修改本地price状态,没有调用Controller注入的onChange方法通知RHF更新字段值
    2. 点击价格盒子修改price时,完全没有同步更新RHF的对应字段值
修复方案

修改后完全保留原有逻辑,Input始终展示price状态值,两个修改函数的运行效果和原来完全一致,仅补充RHF字段同步逻辑即可:

  1. 从useForm的返回值中解构出setValue方法用于手动更新字段
  2. 在两处修改price的位置补充字段同步逻辑
  3. 调整Controller的默认值匹配初始price的空字符串状态
const schema = yup
  .object({
    voucherPrice: yup.number().positive().required(),
  })
  .required();

function PriceSelection(props) {
  const {
    register,
    handleSubmit,
    control,
    setValue, // 新增:解构setValue方法
    formState: { errors },
  } = useForm({
    resolver: yupResolver(schema),
  });
  const onSubmit = (data) => {
    console.log("does not work?", data);
  };

  const classes = useStylesPriceSelection();
  const [selected, setSelected] = useState(false);
  const [price, setPrice] = useState("");

  const handleSelectedBox = (ev) => {
    console.log("", price);
    const newPrice = parseInt(ev.target.getAttribute("data-price"));
    setSelected(ev.target.getAttribute("data-boxprice"));
    setPrice(newPrice);
    setValue("voucherPrice", newPrice); // 新增:同步价格到RHF表单字段
  };

  const handlePriceInputChange = (ev) => {
    const inputValue = ev.target.value;
    const newPrice = inputValue ? parseInt(inputValue) : ""; // 处理空输入避免生成NaN
    console.log("change", newPrice);
    setPrice(newPrice);
  };

  const priceBox1 = 25;
  const priceBox2 = 50;
  const priceBox3 = 75;
  const priceBox4 = 100;

  return (
    <div>
      <section className={classes.priceBoxWrap}>
        <div
          data-boxprice="1"
          data-price={`${priceBox1}`}
          onClick={(ev) => handleSelectedBox(ev)}
          className={clsx(
            classes.priceBox,
            ((selected === "1" && price === priceBox1) ||
              price === priceBox1) &&
              classes.priceBoxSelected
          )}
        >
          {`${priceBox1}€`}
        </div>

        <div
          data-boxprice="2"
          data-price={`${priceBox2}`}
          onClick={(ev) => handleSelectedBox(ev)}
          className={clsx(
            classes.priceBox,
            ((selected === "2" && price === priceBox2) ||
              price === priceBox2) &&
              classes.priceBoxSelected
          )}
        >
          {`${priceBox2}€`}
        </div>
      </section>

      <section className={classes.priceBoxWrap}>
        <div
          data-boxprice="3"
          data-price={`${priceBox3}`}
          onClick={(ev) => handleSelectedBox(ev)}
          className={clsx(
            classes.priceBox,
            ((selected === "3" && price === priceBox3) ||
              price === priceBox3) &&
              classes.priceBoxSelected
          )}
        >
          {`${priceBox3}€`}
        </div>

        <div
          data-boxprice="4"
          data-price={`${priceBox4}`}
          onClick={(ev) => handleSelectedBox(ev)}
          className={clsx(
            classes.priceBox,
            ((selected === "4" && price === priceBox4) ||
              price === priceBox4) &&
              classes.priceBoxSelected
          )}
        >
          {`${priceBox4}€`}
        </div>
      </section>
      <form onSubmit={handleSubmit(onSubmit)}>
        <Controller
          name="voucherPrice"
          control={control}
          defaultValue={""} // 修改:匹配初始price的空字符串状态
          rules={{ required: true }}
          render={({ field: { onChange, value, ref, ...field } }) => (
            <Input 
              {...field} 
              onChange={(ev) => {
                handlePriceInputChange(ev);
                onChange(ev.target.value); // 新增:同步输入值到RHF表单字段
              }} 
              value={price} 
              type="number" 
              innerRef={ref} 
            />
          )}
        />

        <p>{errors.voucherPrice?.message}</p>

        <Button
          variant="contained"
          sx={{ mt: 1, mr: 1 }}
          type="submit"
        >
          {"Continue"}
        </Button>
      </form>
    </div>
  );
}

export default PriceSelection;
排查技巧

后续遇到类似提交不触发的问题,可以给handleSubmit传入第二个回调函数,直接打印校验失败的错误信息,方便快速定位问题:

<form onSubmit={handleSubmit(onSubmit, (errors) => console.log("校验失败:", errors))}>

内容的提问来源于stack exchange,提问作者Gotey

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 22:06:03