React Hook Form中Yup验证错误未触发及显示问题求助
登录表单Yup + React-Hook-Form验证无效问题排查与修复
以下是导致验证不触发、Errors对象为空的核心问题及修复方案:
1. 表单字段未正确注册到React Hook Form
你当前仅在useEffect中调用register,但未将register返回的属性(如name、onChange、onBlur)传递给自定义Input组件。React Hook Form需要通过这些注册属性追踪字段值变化,才能触发验证逻辑。同时,手动使用setValue绑定onChange的方式会绕过表单的自动状态管理,导致验证无法感知输入变化。
修复方式:
- 移除
useEffect中的register调用 - 将
register('字段名')返回的属性解构传入Input组件(确保你的Input组件能接收并传递这些属性到内部的<input>元素)
2. Yup条件验证缺少variant上下文
你的authSchema中使用yup.string().when('variant')做条件验证,但variant是组件的状态而非表单字段,Yup无法直接获取这个值,导致条件验证逻辑不生效。
修复方式:
- 在
useForm配置中添加defaultValues,将variant作为表单的内部字段,供Yup条件验证使用
3. 按钮点击绕过表单验证
你给Button组件添加了onClick事件直接调用登录/注册方法,这会跳过handleSubmit的验证流程,直接执行提交逻辑,导致验证永远不触发。
修复方式:
- 移除
Button的onClick属性,仅保留表单的onSubmit绑定handleSubmit - 给
Button添加type="submit"确保它触发表单提交事件
修改后的完整代码示例
import axios from 'axios' import { useCallback, useState } from 'react' import { getSession, signIn } from 'next-auth/react' import { useForm } from 'react-hook-form' import Button from '../components/Button' import * as yup from 'yup' import { yupResolver } from '@hookform/resolvers/yup' import Input from '../components/Input' export async function getServerSideProps(context) { const session = await getSession(context) if (session) { return { redirect: { destination: '/', permanent: false, }, } } return { props: {}, } } const authSchema = yup.object().shape({ name: yup.string().when('variant', { is: 'register', then: yup .string() .min(3, 'Name must be at least 3 characters') .required('Name is required'), }), email: yup.string().email('Invalid email').required('Email is required'), password: yup .string() .min(6, 'Password must be at least 6 characters') .required('Password is required'), }) const Auth = () => { const [variant, setVariant] = useState('login') const { register, handleSubmit, formState: { errors }, } = useForm({ resolver: yupResolver(authSchema), defaultValues: { variant, name: '', email: '', password: '' } }) const toggleVariant = useCallback(() => { setVariant((currentVariant) => currentVariant === 'login' ? 'register' : 'login' ) }, []) const handleLogin = async (data) => { try { await signIn('credentials', { email: data.email, password: data.password, redirect: false, callbackUrl: '/', }) } catch (error) { console.log(error) } } const handleRegister = async (data) => { try { await axios.post('/api/register', { email: data.email, name: data.name, password: data.password, }) handleLogin(data) } catch (error) { console.log(error) } } return ( <div className="h-screen w-full"> <div className="bg-white w-full h-full lg:bg-opacity-50"> <nav className="px-12 py-5"> <img src="/header.svg" className="h-12" alt="Logo" /> </nav> <div className="flex justify-center"> <form className="bg-white px-16 py-16 self-center mt-2 lg:w-2/5 lg:max-w-md rounded-md w-full" onSubmit={handleSubmit( variant === 'login' ? handleLogin : handleRegister )} > <h2 className="text-zinc-500 text-4xl mb-8 font-semibold"> {variant === 'login' ? 'Sign in' : 'Register'} </h2> <div className="flex flex-col gap-4"> {variant === 'register' && ( <div> {errors.name && ( <p className="text-red-500">{errors.name.message}</p> )} <Input id="name" type="text" label="Name" {...register('name')} /> </div> )} {errors.email && <p className="text-red-500">{errors.email.message}</p>} <Input id="email" type="email" label="Email Address" {...register('email')} /> {errors.password && <p className="text-red-500">{errors.password.message}</p>} <Input type="password" id="password" label="Password" {...register('password')} /> </div> <Button primary modifier={'w-full mt-16'} type="submit" > {variant === 'login' ? 'Login' : 'Sign up'} </Button> <p className="text-neutral-500 mt-12"> {variant === 'login' ? 'New to the application?' : 'Already have an account?'} <span onClick={toggleVariant} className="text-neutral-500 ml-1 hover:underline cursor-pointer" > {variant === 'login' ? 'Create an account' : 'Login'} </span> </p> </form> </div> </div> </div> ) } export default Auth
额外注意:确保你的自定义Input组件能正确接收并传递属性到原生<input>,示例如下:
const Input = ({ id, label, ...props }) => { return ( <div className="flex flex-col gap-1"> <label htmlFor={id} className="text-sm text-zinc-600">{label}</label> <input id={id} className="border rounded-md px-3 py-2" {...props} /> </div> ) }
内容的提问来源于stack exchange,提问作者Dodecadoofus
相关产品推荐
相关产品推荐

