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

如何在React-hook-form的Controller中重新验证Joi.ref()?

问题描述

我有两个Controller,分别对应开始日期和结束日期,每个Controller包含月份和年份两个输入框。结束日期必须始终晚于开始日期,我使用Joi进行该验证。问题在于,react-hook-form仅在结束日期的onBlur事件时执行min(Joi.ref("startDate"))验证,而不会在开始日期的onChange或onBlur事件时触发验证。

现有代码

schema = Joi.object({
      startDate: Joi.date()
        .raw()
        .format("YYYY-MM-DD")
        .less("now")
        .greater("1920-1-1"),
      endDate: Joi.date()
        .raw()
        .format("YYYY-MM-DD")
        .less("now")
        .min(Joi.ref("startDate"))
        .allow(null)
});


const MonthYearInput = ({ name, checkbox }) => {
  const { control } = useFormContext();
  const monthRef = useRef();
  const yearRef = useRef();

  const getOptions = (list) => {
    if (!list) return null;
    const options = list.map((item) => {
      return (
        <option value={item.id} key={item.id}>
          {item.name}
        </option>
      );
    });
    return options;
  };

  const getDate = () => {
    const year = yearRef.current.value;
    const month = monthRef.current.value;
    const formattedMonth = month < 10 ? `0${month}` : month;
    return `${year}-${formattedMonth}-01`;
  };

  return (
    <>
      <Controller
        control={control}
        name={name}
        defaultValue=""
        render={({ field, fieldState: { error } }) => {
          return (
            <>
              <select
                className="form-select"
                onChange={(e) => {
                  field.onChange(getDate());
                }}
                onBlur={field.onBlur}
                ref={monthRef}
                name={`${name}month`}
                defaultValue=""
              >
                <option key="0" value="" disabled>
                  Month
                </option>
                {getOptions(months)}
              </select>
              <input
                name={`${name}year`}
                type="number"
                step={1}
                onChange={(e) => {
                  field.onChange(getDate());
                }}
                onBlur={field.onBlur}
                ref={yearRef}
                placeholder="Year"
              />
              {error?.message && <div>{error?.message}</div>}
            </>
          );
        }}
      />
    </>
  );
};
解决方案

核心思路是在开始日期发生变化时,手动触发结束日期字段的验证,利用react-hook-form提供的trigger方法即可实现。

修改步骤

  1. 从useFormContext中解构出trigger方法
  2. 给开始日期的输入框绑定统一的变化处理函数,在更新字段值后触发结束日期的验证

修改后的组件代码

const MonthYearInput = ({ name, checkbox }) => {
  // 新增解构trigger方法
  const { control, trigger } = useFormContext();
  const monthRef = useRef();
  const yearRef = useRef();

  const getOptions = (list) => {
    if (!list) return null;
    const options = list.map((item) => {
      return (
        <option value={item.id} key={item.id}>
          {item.name}
        </option>
      );
    });
    return options;
  };

  const getDate = () => {
    const year = yearRef.current.value;
    const month = monthRef.current.value;
    const formattedMonth = month < 10 ? `0${month}` : month;
    return `${year}-${formattedMonth}-01`;
  };

  return (
    <>
      <Controller
        control={control}
        name={name}
        defaultValue=""
        render={({ field, fieldState: { error } }) => {
          // 统一处理输入变化,包含触发验证逻辑
          const handleChange = () => {
            field.onChange(getDate());
            // 仅当当前是开始日期字段时,触发结束日期的验证
            if (name === 'startDate') {
              trigger('endDate');
            }
          };

          return (
            <>
              <select
                className="form-select"
                onChange={handleChange}
                onBlur={field.onBlur}
                ref={monthRef}
                name={`${name}month`}
                defaultValue=""
              >
                <option key="0" value="" disabled>
                  Month
                </option>
                {getOptions(months)}
              </select>
              <input
                name={`${name}year`}
                type="number"
                step={1}
                onChange={handleChange}
                onBlur={field.onBlur}
                ref={yearRef}
                placeholder="Year"
              />
              {error?.message && <div>{error?.message}</div>}
            </>
          );
        }}
      />
    </>
  );
};

说明

当用户修改开始日期的月份或年份时,handleChange函数会先更新startDate的字段值,随后调用trigger('endDate')触发结束日期的Joi验证,确保结束日期始终晚于最新的开始日期,不影响原有验证逻辑的同时实现实时校验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 11:43:30