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

如何在React-Hook-Form中同时实现输入验证与状态保存?

问题解决:React-Hook-Form 集成后自定义状态与onChange失效

问题根源

React-Hook-Form(RHF)的核心设计是接管表单状态管理,你手动添加的useState和onChange会和RHF的内部状态管理冲突:

  • register函数会自动覆盖组件的onChange属性,导致你的handleChange无法触发
  • RHF不会同步更新你自己定义的stateRegister,所以console.log看不到最新值

解决方案

完全依托RHF的状态管理机制,移除自定义的useState和onChange,用RHF提供的API获取表单值:

1. 移除冗余状态与事件处理

删掉stateRegister、repassword这两个useState,以及对应的handleChange、handleConfirmPassword函数。

2. 用RHF的API获取表单值

  • 使用watch监听实时输入值
  • 使用getValues在需要时获取指定字段值
  • 如需向父组件同步表单状态,可通过watch触发回调

3. 确认密码的验证处理

给确认密码字段添加register,并通过自定义验证规则匹配密码字段值

修改后的完整代码

import * as React from 'react'
import Grid from '@mui/material/Grid'
import Typography from '@mui/material/Typography'
import TextField from '@mui/material/TextField'
import FormControlLabel from '@mui/material/FormControlLabel'
import Checkbox from '@mui/material/Checkbox'
import propTypes from 'prop-types'
import { useForm, watch } from 'react-hook-form'

export default function BasicForm(props) {
  const {
    register,
    formState: { errors },
    getValues,
  } = useForm({
    mode: 'all',
    defaultValues: {
      username: '',
      login: '',
      password: '',
      repassword: '',
      saveLogin: false
    }
  })

  // 监听表单变化,同步给父组件
  const formValues = watch()
  React.useEffect(() => {
    props.stateOfRegister(formValues)
  }, [formValues, props.stateOfRegister])

  console.log('当前表单值:', formValues)
  console.log('errors: ', errors)

  return (
    <React.Fragment>
      <Typography variant="h6" gutterBottom>
        注册信息填写
      </Typography>
      <Typography variant="caption" color="red" gutterBottom>
        所有字段为必填项 *
      </Typography>
      <Grid container spacing={3}>
        <Grid item xs={12}>
          <TextField
            required
            id="username"
            name="username"
            label="用户名"
            fullWidth
            autoComplete="username"
            variant="standard"
            {...register('username', {
              required: '用户名不能为空',
              minLength: { value: 3, message: '用户名至少3个字符' },
              maxLength: { value: 30, message: '用户名最多30个字符' },
            })}
            helperText={errors.username?.message}
          />
        </Grid>
        <Grid item xs={12}>
          <TextField
            required
            id="login"
            name="login"
            label="邮箱"
            type="email"
            fullWidth
            autoComplete="email"
            variant="standard"
            {...register('login', {
              required: '邮箱不能为空',
              pattern: {
                value: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
                message: '请输入有效的邮箱地址',
              },
            })}
            helperText={errors.login?.message}
          />
        </Grid>
        <Grid item xs={12} sm={6}>
          <TextField
            required
            id="password"
            name="password"
            label="密码"
            type="password"
            fullWidth
            autoComplete="new-password"
            variant="standard"
            {...register('password', {
              required: '密码不能为空',
              pattern: {
                value: /^(?=.*[0-9])(?=.*[!@#$%^&*.,])[a-zA-Z0-9!@#$%^&*.,]{6,16}$/,
                message: '密码必须包含至少6个字符,且包含大小写字母、数字和特殊字符',
              },
            })}
            helperText={errors.password?.message}
          />
        </Grid>
        <Grid item xs={12} sm={6}>
          <TextField
            required
            id="repassword"
            name="repassword"
            label="确认密码"
            type="password"
            fullWidth
            autoComplete="new-password"
            variant="standard"
            {...register('repassword', {
              required: '请再次输入密码',
              validate: value => value === getValues('password') || '两次输入的密码不一致',
            })}
            helperText={errors.repassword?.message}
          />
        </Grid>
        <Grid item xs={12}>
          <FormControlLabel
            control={<Checkbox color="secondary" {...register('saveLogin')} />}
            label="保持登录状态"
          />
        </Grid>
      </Grid>
    </React.Fragment>
  )
}

BasicForm.propTypes = {
  stateOfRegister: propTypes.func,
}

关键修改点说明

  • 用RHF的defaultValues替代自定义初始状态
  • 移除所有自定义onChange,完全由register接管输入处理
  • 通过watch实时获取表单值,并通过useEffect同步给父组件
  • 确认密码的验证通过register的validate规则实现,调用getValues获取密码字段值
  • 复选框也通过register绑定,无需手动处理状态

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 21:20:37