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

react-hook-form v7如何实现表单校验、提交及错误提示?

react-hook-form v7 结合 react-bootstrap 表单问题解决方案

问题1:配置全量校验通过后再提交API

你不需要额外写onChange触发提交的逻辑,react-hook-form自带的handleSubmit方法默认就会先执行全量表单规则校验,只有所有字段校验全部通过时,才会执行你传入的提交回调函数,完全满足“校验通过才发API请求”的需求。
你考虑的onChange={handel(on submit)} onSubmit={handleOnSubmit}写法是错误的,会导致输入框值每次变化都触发提交逻辑,完全不符合正常表单交互逻辑。
Next.js环境下正确的提交配置参考:

// 从react-hook-form导入所需方法
import { useForm } from "react-hook-form";

export default function ContactForm() {
  // 初始化表单方法,可按需配置校验触发时机
  const { register, handleSubmit, formState: { errors } } = useForm({
    mode: 'onSubmit', // 默认值,点击提交按钮时才触发全量校验,需要输入时实时校验可改为'onChange'
    reValidateMode: 'onChange'
  });

  // 提交逻辑:仅当全量校验全部通过时,该函数才会执行
  const submitForm = async (formData) => {
    // 此处直接写API调用逻辑即可,formData为所有校验通过的字段值集合
    const res = await fetch('/api/contact', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(formData)
    })
    // 后续接口响应处理逻辑自行补充
  }

  return (
    <form onSubmit={handleSubmit(submitForm)}>
      {/* 表单字段放置处 */}
      <button type="submit">提交</button>
    </form>
  )
}

注意:不要给输入框单独绑定自定义onChange覆盖register注入的逻辑,除非你明确知晓如何和RHF内部状态同步,否则会出现状态错乱问题。


问题2:错误提示无法正常渲染

你的代码存在3个核心错误,直接导致错误信息无法读取、渲染:

  • 字段名不匹配:register的第一个参数必须和ErrorMessage组件的name属性完全一致,你写的是register("Mobile number", {...}),但ErrorMessage读取的是name="phone",字段名不匹配的情况下,RHF无法将校验规则和对应字段绑定,自然读不到错误信息。
  • 状态逻辑冲突:你给输入框同时绑定了{...register()},又手动添加了value={phone} onChange={(e) => { setPhone(e.target.value) }},相当于同时使用RHF的非受控状态和自定义本地state,手动绑定的onChange会覆盖register注入的状态更新逻辑,导致RHF无法获取输入值、也无法触发校验状态更新。
  • 组件适配问题:如果你使用的是react-bootstrap的表单控件,直接将适配原生DOM的register属性绑定在自定义组件上可能存在属性透传失败的问题,建议要么使用原生input标签,要么用RHF提供的Controller组件包裹第三方UI组件。

修正后的手机号字段代码

import { Form } from 'react-bootstrap';
import { useForm } from 'react-hook-form';
// 注意ErrorMessage需要从@hookform/error-message导入
import { ErrorMessage } from '@hookform/error-message';

// 表单内手机号字段部分
<div className="m-t-15">
  <Form.Control
    style={{width: "100%"}}
    type="tel"
    placeholder="Wpisz swój telefon"
    // register第一个参数和下方ErrorMessage的name保持一致,统一为phone
    {...register("phone", {
      required: {
        value: true,
        message: "Numer telefonu jest obowiązkowy, zebysmy mogli sie skontaktować"
      }, 
      pattern: {
        value: /^[0-9+-]+$/,
        message: "Dozwolone sa wylacznie cyfry"
      }, 
      minLength: {
        value: 9,
        message: "Numer telefonu jest za krótki"
      }, 
      maxLength: {
        value: 15,
        message: "Numer telefonu jest za długi"
      }
    })}
  />
  <ErrorMessage
    errors={errors}
    name="phone"
    render={({ messages }) => {
      return messages ? 
        Object.entries(messages).map(([type, message]) => (
          <p key={type} className="text-danger small mt-1">{message}</p>
        )) : null
    }}
  />
</div>

关键注意事项

  • 删掉和表单字段重复的本地state声明,比如const [phone, setPhone] = useState(''),RHF会自行管理表单值,不需要额外声明state存储。需要读取字段值时,用watch('phone')获取即可,提交时也可以直接从入参formData里拿到对应值。
  • 如果确实需要在值变化时执行自定义逻辑(比如手机号格式化),可以把逻辑写在register的onChange配置项里,不要直接覆盖输入框的onChange属性:
    {...register("phone", {
      // 其余校验规则保持不变
      onChange: (e) => {
        // 此处写自定义逻辑即可,不需要手动setState同步值
        console.log('当前输入值', e.target.value)
      }
    })}
    
  • 确保传入ErrorMessage的errors对象是从useForm返回的formState中解构得到的,不要自行声明errors变量。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 08:27:03