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

react-hook-form新增表单首次提交校验失败求助

React Hook Form 新增表单提交校验异常问题

问题现象

点击+按钮新增一条表单,TIME输入框默认填充当前时间,但首次提交新增表单时,校验提示TIME字段未输入(页面已显示默认值),二次提交则正常传值。

代码逻辑说明

点击+按钮调用handlerAddTr函数,接收索引参数,在现有list状态的index+1位置插入新数据对象,通过setList更新数组触发重渲染,页面已显示最新数据;尝试过函数式更新,问题仍存在。


相关代码片段

main.jsx

const [list, setList] = useState(data.result); // 通过loader获取数据

const handlerAddTr = (e, index, schedule) => {
    e.preventDefault();

    const newItem = {
      NO: schedule.NO,
      DAY: "",
      YOIL: "",
      HOLYDAY_YN: "",
      HOLYDAY: "",
      TIME: timeString, // 当前时间
      TYPE: 1,
      GOV_NAME: "",
      GOV_DEPT: "",
      RQU_CONTENT: "",
      RQU_NAME: "", 
      RQU_TEL: "",
      SERVER: "",
      WEB: "",
      VISIT_ETC: "",
      RESULT: 1,
      WORK_NAME: "",
      HOLY_NAME: "",
      METTING: "",
      ETC: "",
      COPY: true,
    };

    const newItems = [...list];
    newItems.splice(index + 1, 0, newItem);
    setList(newItems);
  };

return (
    <div>
        {list.length !== 0 ? (
          list.map((day, index) => (
            <SchedulerList
              Key={day.NO}
              day={day}
              index={index}
              handlerAddTr={handlerAddTr}
              handlerMinusTr={handlerMinusTr}
              handlerTime={handlerTime}

              updateTime={updateTime}
              onSubmit={onSubmit}
            />
          ))
        ) : (
          <tr>
            <td colspan="16">no data.</td>
          </tr>
        )}
      </div>
);

list.jsx

const List = ({
  Key,
  day,
  index,
  handlerAddTr,
}) => {
  const {
    handleSubmit,
    formState: { errors },
    register,
  } = useForm();

  const callSubmit = (e) => {
    if (e.keyCode === 13) {
      handleSubmit(onSubmit)();
    }
  };

  return (
    <form className="flex" onSubmit={handleSubmit(onSubmit)}>
      <div>
        <input type="hidden" name="NO" value={day.NO} />
        <span className="input">
          {day.DAY} {day.YOIL}{" "}
          {!day.COPY ? (
            <button type="button" onClick={(e) => handlerAddTr(e, index, day)}>
              +
            </button>
          ) : (
            <button type="button" onClick={() => handlerMinusTr(index, day)}>
              -
            </button>
          )}
          <br></br>
          {!day.HOLYDAY ? "" : `(${day.HOLYDAY})`}
        </span>
      </div>

      <div>
        <input
          type="text"
          name="TIME"
          {...register("TIME", {
            required: "Please enter the time",
            pattern: {
              value: /^([01][0-9]|2[0-3]):([0-5][0-9])$/g,
              message: "Please enter it in time format HH:mm",
            },
          })}
          defaultValue={day.TIME}
          className="input"
          onClick={() => handlerTime(index)}
        />
        <p>{errors.TIME?.message}</p>
      </div>
    </form>
  );
};

问题原因与解决方案

核心原因

  1. React Hook Form 状态未同步初始值:useForm仅在组件挂载时读取一次defaultValue,新增表单的TIME默认值虽显示在页面,但未同步到RHF的内部校验状态,导致首次提交时判定字段为空。
  2. 列表key属性错误:原代码使用Key={day.NO}(大写K),React的列表标识必须是小写key,这会导致组件复用逻辑异常,加剧状态不同步问题。

解决方案

方案1:父组件统一管理表单状态(推荐)

将useForm提升到父组件,用数组字段管理所有条目的表单状态,新增条目时直接同步初始值到RHF:

父组件改造(main.jsx)
import { useForm } from "react-hook-form";

const [list, setList] = useState(data.result);
const { register, handleSubmit, setValue, formState: { errors } } = useForm({
  defaultValues: { schedules: list }
});

const handlerAddTr = (e, index, schedule) => {
  e.preventDefault();
  const timeString = new Date().toTimeString().slice(0,5); // 示例当前时间格式
  const newItem = {
    // ...原newItem属性,TIME值为timeString
  };

  const newItems = [...list];
  newItems.splice(index + 1, 0, newItem);
  setList(newItems);
  // 同步新增条目的TIME初始值到RHF
  setValue(`schedules[${index+1}].TIME`, timeString);
};

return (
  <form onSubmit={handleSubmit(onSubmit)}>
    {list.length !== 0 ? (
      list.map((day, index) => (
        <SchedulerList
          key={day.NO} // 修正为小写key
          day={day}
          index={index}
          handlerAddTr={handlerAddTr}
          register={register}
          errors={errors}
          // ...其他props
        />
      ))
    ) : (
      <tr><td colspan="16">no data.</td></tr>
    )}
    <button type="submit">提交</button>
  </form>
);
子组件改造(list.jsx)
const List = ({
  key,
  day,
  index,
  handlerAddTr,
  register,
  errors
}) => {
  return (
    <div className="flex">
      <div>
        <input type="hidden" {...register(`schedules[${index}].NO`)} value={day.NO} />
        {/* ...原按钮与日期展示代码 */}
      </div>

      <div>
        <input
          type="text"
          {...register(`schedules[${index}].TIME`, {
            required: "Please enter the time",
            pattern: {
              value: /^([01][0-9]|2[0-3]):([0-5][0-9])$/,
              message: "Please enter it in time format HH:mm",
            },
          })}
          className="input"
          onClick={() => handlerTime(index)}
        />
        <p>{errors?.schedules?.[index]?.TIME?.message}</p>
      </div>
    </div>
  );
};

方案2:子组件手动同步初始值(保留独立useForm)

若需保留子组件独立的useForm实例,在组件挂载时用setValue同步初始值:

import { useEffect } from "react";

const List = ({ day }) => {
  const { register, setValue, errors, handleSubmit } = useForm();

  useEffect(() => {
    // 挂载时同步TIME初始值到RHF状态
    setValue("TIME", day.TIME);
  }, [setValue, day.TIME]);

  // ...其余代码不变,移除defaultValue属性
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 16:00:41