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

使用react-hook-form+react-query实现日期筛选表单的疑难

解决React Hook Form + React Query 日期筛选表单的有效查询问题

核心解决思路

只在表单通过Zod校验后更新查询参数,用React Query的enabled选项控制查询触发时机,既保证只有合法的日期范围才会发起请求,又解决初始自动查询、无效请求等问题。

完整实现代码

import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { trpc } from '../utils/trpc'; // 替换成你的tRPC客户端路径

// Zod校验规则:结束日期必须晚于起始日期,且两个日期都必填
const DateRangeSchema = z.object({
  startDate: z.date({ required_error: '起始日期不能为空' }),
  endDate: z.date({ required_error: '结束日期不能为空' })
    .refine((_, ctx) => {
      const { startDate, endDate } = ctx.data;
      return !startDate || !endDate || endDate > startDate;
    }, '结束日期必须晚于起始日期')
});

type DateRange = z.infer<typeof DateRangeSchema>;

export default function EventFilter() {
  // 只存通过校验的查询参数,初始为null表示没有有效查询条件
  const [validQuery, setValidQuery] = useState<DateRange | null>(null);

  const {
    register,
    handleSubmit,
    formState: { errors, isSubmitting }
  } = useForm<DateRange>({
    resolver: zodResolver(DateRangeSchema),
    mode: 'onSubmit' // 只在提交时校验,减少不必要的状态波动
  });

  // React Query:只有当有有效查询参数时才发起请求
  const eventsQuery = trpc.bookings.events.useQuery(validQuery!, {
    enabled: !!validQuery, // 无有效参数时不触发查询
    keepPreviousData: true // 切换条件时保留旧数据,避免空白加载
  });

  // 提交回调:只有校验通过的values才会走到这里
  const handleFilterSubmit = (values: DateRange) => {
    setValidQuery(values);
  };

  return (
    <form onSubmit={handleSubmit(handleFilterSubmit)}>
      <div>
        <label>起始日期</label>
        <input
          type="date"
          {...register('startDate', { valueAsDate: true })}
        />
        {errors.startDate && <p className="error">{errors.startDate.message}</p>}
      </div>
      <div>
        <label>结束日期</label>
        <input
          type="date"
          {...register('endDate', { valueAsDate: true })}
        />
        {errors.endDate && <p className="error">{errors.endDate.message}</p>}
      </div>
      <button type="submit" disabled={isSubmitting || eventsQuery.isLoading}>
        {eventsQuery.isLoading ? '加载中...' : '筛选事件'}
      </button>

      {/* 事件列表展示 */}
      {eventsQuery.isSuccess && (
        <ul className="event-list">
          {eventsQuery.data.map(event => (
            <li key={event.id}>
              {event.title} - {new Date(event.date).toLocaleDateString()}
            </li>
          ))}
        </ul>
      )}
      {eventsQuery.isError && <p className="error">加载失败:{eventsQuery.error.message}</p>}
    </form>
  );
}

为什么这个方案能解决你的问题

  • 避免初始自动查询:初始validQuery为null,enabled: false让React Query在组件挂载时不发起请求,完全符合“用户手动填写后再查询”的需求。
  • 杜绝无效请求:handleSubmit会自动拦截未通过Zod校验的提交,只有合法的日期范围才会更新validQuery,确保查询参数100%符合规则。
  • 符合HTTP规范:依然用useQuery发起GET请求,不用改用Mutation破坏REST规范。
  • 优化用户体验:keepPreviousData保留上一次的查询结果,切换筛选条件时不会出现页面空白,加载状态更友好。

对应你之前方案的问题修正

  1. 方案1的初始查询问题:不需要强制设置初始有效值,用validQuery的null状态+enabled控制,彻底避免初始自动查询。
  2. 方案2的无效查询问题:放弃实时监听表单值,改为仅在提交时更新有效参数,不会在用户输入过程中发起无效请求。
  3. 方案3的缓存自动更新问题:查询只依赖validQuery,表单临时修改不会触发查询,只有提交后的有效值才会触发更新。
  4. 方案4的请求规范问题:坚持使用useQuery,完全符合GET请求用于数据查询的设计原则。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 07:30:52