react-hook-form表单校验:如何实现至少一个输入字段有值?
React Hook Form 实现「至少一个字段有值」的校验逻辑
要实现「只要至少一个输入字段有值,表单就有效」的校验,有两种常用方案,具体如下:
方案一:表单级全局校验
利用 useForm 的 validate 选项,在表单提交时统一检查所有目标字段是否全为空,若全为空则返回错误信息。
示例代码:
import { useForm, Controller } from 'react-hook-form'; import TextField from '@mui/material/TextField'; function App() { const { control, handleSubmit, formState: { errors } } = useForm({ defaultValues: { value1: '', value2: '', value3: '' }, validate: (data) => { const errors = {}; // 检查所有字段是否都为空 const allFieldsEmpty = Object.values(data).every(val => !val.trim()); if (allFieldsEmpty) { // 可将错误绑定到任意字段,或多个字段 errors.value1 = '至少需要填写一个字段'; } return errors; } }); const onSubmit = (data) => { console.log('提交的数据:', data); }; return ( <form onSubmit={handleSubmit(onSubmit)}> <Controller control={control} name="value1" render={({ field }) => ( <TextField {...field} fullWidth sx={{ maxWidth: 600, mb: 2 }} label="Value 1" margin="dense" error={!!errors.value1} helperText={errors.value1?.message} /> )} /> <Controller control={control} name="value2" render={({ field }) => ( <TextField {...field} fullWidth sx={{ maxWidth: 600, mb: 2 }} label="Value 2" margin="dense" /> )} /> <Controller control={control} name="value3" render={({ field }) => ( <TextField {...field} fullWidth sx={{ maxWidth: 600, mb: 2 }} label="Value 3" margin="dense" /> )} /> <button type="submit">提交表单</button> </form> ); } export default App;
这种方案适合只需要在提交时做一次全局校验的场景,错误提示可以集中显示在某个字段或单独的提示区域。
方案二:字段级联动校验
通过 watch 监听所有目标字段的值,给每个字段添加自定义校验规则,实时检查是否有至少一个字段有值。
示例代码:
import { useForm, Controller } from 'react-hook-form'; import TextField from '@mui/material/TextField'; function App() { const { control, handleSubmit, formState: { errors }, watch } = useForm({ defaultValues: { value1: '', value2: '', value3: '' } }); // 监听需要校验的字段组 const targetFields = watch(['value1', 'value2', 'value3']); const onSubmit = (data) => { console.log('提交的数据:', data); }; // 自定义校验规则:只要有一个字段非空就通过 const atLeastOneFilled = () => { const hasValidValue = targetFields.some(val => val.trim()); return hasValidValue || '至少需要填写一个字段'; }; return ( <form onSubmit={handleSubmit(onSubmit)}> <Controller control={control} name="value1" rules={{ validate: atLeastOneFilled }} render={({ field }) => ( <TextField {...field} fullWidth sx={{ maxWidth: 600, mb: 2 }} label="Value 1" margin="dense" error={!!errors.value1} helperText={errors.value1?.message} /> )} /> <Controller control={control} name="value2" rules={{ validate: atLeastOneFilled }} render={({ field }) => ( <TextField {...field} fullWidth sx={{ maxWidth: 600, mb: 2 }} label="Value 2" margin="dense" error={!!errors.value2} helperText={errors.value2?.message} /> )} /> <Controller control={control} name="value3" rules={{ validate: atLeastOneFilled }} render={({ field }) => ( <TextField {...field} fullWidth sx={{ maxWidth: 600, mb: 2 }} label="Value 3" margin="dense" error={!!errors.value3} helperText={errors.value3?.message} /> )} /> <button type="submit">提交表单</button> </form> ); } export default App;
这种方案支持实时校验,用户输入时就能触发规则检查,适合需要即时反馈的场景。
额外优化建议
如果字段数量较多,可以将字段名存入数组,通过循环渲染 Controller 和 TextField,减少重复代码:
const fieldNames = ['value1', 'value2', 'value3']; // 渲染部分 {fieldNames.map(name => ( <Controller key={name} control={control} name={name} rules={{ validate: atLeastOneFilled }} render={({ field }) => ( <TextField {...field} fullWidth sx={{ maxWidth: 600, mb: 2 }} label={name.charAt(0).toUpperCase() + name.slice(1)} margin="dense" error={!!errors[name]} helperText={errors[name]?.message} /> )} /> ))}
内容的提问来源于stack exchange,提问作者user3142695
相关产品推荐
相关产品推荐

