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> ); };
问题原因与解决方案
核心原因
- React Hook Form 状态未同步初始值:
useForm仅在组件挂载时读取一次defaultValue,新增表单的TIME默认值虽显示在页面,但未同步到RHF的内部校验状态,导致首次提交时判定字段为空。 - 列表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
相关产品推荐
相关产品推荐

