react-hook-form中watch提交前不更新值,IONIC注册表单验证求助
Ionic React注册表单:解决邮箱唯一性验证与提交跳转问题
核心需求回顾
点击提交按钮「Passer à mon adhésion」时,需同时满足两个条件才跳转至下一页:
- 表单通过Yup的所有字段验证
- 输入的邮箱未在数据库中注册(
userData为undefined)
若邮箱已被使用,则在表单中显示错误提示。
问题根源分析
你之前尝试的watch/useWatch、getValues方案,核心问题是邮箱唯一性查询的时机不对:要么是提交后才触发查询(导致跳转先执行,查询结果滞后),要么是查询状态与表单验证不同步(需要两次提交才能触发错误提示)。
最优解决方案
将邮箱唯一性验证整合到React Hook Form的异步验证流程中,同时实时监听邮箱输入触发查询,确保提交时验证状态完全同步。
1. 用Yup添加异步邮箱唯一性验证规则
直接在Yup的表单校验schema中加入异步验证,让React Hook Form自动处理验证流程,无需手动判断userData:
import * as yup from 'yup'; const registrationSchema = yup.object().shape({ email: yup.string() .email('请输入有效的邮箱地址') .required('邮箱不能为空') // 自定义异步验证:检查邮箱是否已注册 .test( 'unique-email', '该邮箱已被注册', async (email) => { if (!email) return true; // 空值时跳过,由required规则处理 try { const response = await fetch(`/api/check-email?email=${encodeURIComponent(email)}`); const data = await response.json(); return data.isAvailable; // 接口返回true表示邮箱未被注册 } catch (err) { console.error('邮箱校验失败:', err); return false; // 网络错误时默认视为验证失败 } } ), // 其他字段的校验规则... });
2. 初始化React Hook Form并关联校验规则
使用useForm的resolver绑定Yup schema,让表单自动处理同步/异步验证:
import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; const RegistrationForm = () => { const { register, handleSubmit, formState: { errors, isSubmitting }, } = useForm({ resolver: yupResolver(registrationSchema), mode: 'onSubmit', // 仅在提交时触发验证(也可设为onChange实时验证) }); // 提交成功后的跳转逻辑 const onSubmit = async (data: FormData) => { // 能走到这里说明所有验证(包括邮箱唯一性)都通过了 window.location.href = '/enroll'; // 或者用Ionic的useNavigate跳转 }; return ( <form onSubmit={handleSubmit(onSubmit)}> <ion-input type="email" placeholder="邮箱地址" {...register('email')} ></ion-input> {/* 显示邮箱字段的错误提示 */} {errors.email && <ion-text color="danger">{errors.email.message}</ion-text>} {/* 其他表单字段... */} <ion-button type="submit" disabled={isSubmitting} > {isSubmitting ? '验证中...' : 'Passer à mon adhésion'} </ion-button> </form> ); }; export default RegistrationForm;
3. 可选:实时监听邮箱输入提前触发验证
如果希望用户输入邮箱时就实时校验唯一性,可以把mode设为onChange,或者用useWatch手动触发查询:
import { useWatch } from 'react-hook-form'; const RegistrationForm = () => { const { register, handleSubmit, formState: { errors, isSubmitting }, setError, clearErrors } = useForm({ resolver: yupResolver(registrationSchema), mode: 'onChange', }); const email = useWatch({ name: 'email' }); useEffect(() => { if (!email) return; // 清除之前的邮箱错误 clearErrors('email'); const checkEmailUnique = async () => { try { const response = await fetch(`/api/check-email?email=${encodeURIComponent(email)}`); const data = await response.json(); if (!data.isAvailable) { setError('email', { message: '该邮箱已被注册' }); } } catch (err) { setError('email', { message: '邮箱校验失败,请重试' }); } }; // 防抖处理:避免输入时频繁请求 const timeoutId = setTimeout(checkEmailUnique, 500); return () => clearTimeout(timeoutId); }, [email, setError, clearErrors]); // ... 其余代码不变 };
为什么之前的方案失效?
getValues方案:getValues同步获取字段值,但邮箱唯一性查询是异步的,第一次提交时查询还未完成,userData仍为undefined,导致提前跳转;第二次提交时查询结果已返回,才会触发错误提示。watch/useWatch方案:如果仅在提交回调中触发查询,提交动作会先执行跳转,查询结果滞后返回,导致返回后再次提交才显示错误。- 禁用提交按钮方案:若仅在提交时检测邮箱变化,用户输入邮箱后未触发提交,按钮状态不会更新,无法实时反映验证状态。
内容的提问来源于stack exchange,提问作者Welyweloo
相关产品推荐
相关产品推荐

