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

react-hook-form如何实现两个表单项二选一的必填校验

实现二选一必填校验的方案

要实现两个字段至少填一个的校验逻辑,按以下步骤修改代码即可:

步骤1:调整useForm配置

从useForm中解构出getValues(用于获取其他字段的值)和formState.errors(用于展示错误提示),同时移除两个表单项上自带的required属性,因为我们要自定义校验规则,不需要原生的必填校验。

步骤2:封装自定义校验规则

我们可以封装一个通用的二选一必填校验函数,同时适配普通输入框和文件上传框的取值判断:

  • 普通文本类字段判断是否为空字符串、是否全是空格
  • 文件上传类字段判断选中的文件列表长度是否大于0

步骤3:绑定校验规则和错误提示

给每个Controller添加rules配置绑定校验逻辑,同时在表单项下方增加错误展示区域,让用户明确知道校验失败原因。

修改后的完整代码

import { useForm, Controller } from "react-hook-form";
import {
  Col,
  Row,
  Form,
  FormGroup,
  InputGroup,
  Input,
  Container,
  FormFeedback
} from "reactstrap";

export default function App() {
  const onSubmit = (data) => {
    console.log(data);
  };

  const { control, handleSubmit, getValues, formState: { errors } } = useForm({
    mode: "onChange" // 可选,配置为onChange即可实时校验
  });

  // 通用二选一必填校验方法
  const requireEither = (currentField, otherField, errorMsg) => {
    return (value) => {
      const otherValue = getValues(otherField);
      // 判断当前字段是否有有效值
      let currentValid = !!value?.trim();
      if (currentField === "itemlist2") {
        currentValid = value?.length > 0;
      }
      // 判断另一个字段是否有有效值
      let otherValid = !!otherValue?.trim();
      if (otherField === "itemlist2") {
        otherValid = otherValue?.length > 0;
      }
      // 两个字段都无有效值才返回错误
      return currentValid || otherValid ? true : errorMsg;
    }
  }

  return (
    <Container>
      <Form onSubmit={handleSubmit(onSubmit)}>
        <Row className="m-3">
          <Col>
            <Controller
              name="itemlist"
              control={control}
              rules={{
                validate: requireEither("itemlist", "itemlist2", "请至少填写itemlist或上传文件")
              }}
              render={({ field: { ref, ...field } }) => (
                <div>
                  <Input
                    {...field}
                    type="textarea"
                    rows={10}
                    placeholder="itemlist"
                    innerRef={ref}
                    invalid={!!errors.itemlist}
                  />
                  {errors.itemlist && <FormFeedback>{errors.itemlist.message}</FormFeedback>}
                </div>
              )}
            />
          </Col>
        </Row>
        <Row className="m-3">
          <Col>
            <FormGroup row className="mr-md-1">
              <InputGroup className="mb-3">
                <Controller
                  name="itemlist2"
                  control={control}
                  rules={{
                    validate: requireEither("itemlist2", "itemlist", "请至少上传文件或填写itemlist")
                  }}
                  render={({ field: { ref, ...field } }) => (
                    <div>
                      <Input
                        {...field}
                        type="file"
                        innerRef={ref}
                        invalid={!!errors.itemlist2}
                      />
                      {errors.itemlist2 && <FormFeedback>{errors.itemlist2.message}</FormFeedback>}
                    </div>
                  )}
                />
              </InputGroup>
            </FormGroup>
          </Col>
        </Row>
        <button type="submit" className="btn btn-primary m-3">提交</button>
      </Form>
    </Container>
  );
}

补充说明

如果你的项目中已经接入了Yup、Zod等校验库,也可以直接在schema中配置条件必填逻辑,实现效果完全一致。上述封装的requireEither函数可以复用,后续有其他二选一必填的场景也可以直接调用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 04:36:05