使用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的初始查询问题:不需要强制设置初始有效值,用
validQuery的null状态+enabled控制,彻底避免初始自动查询。 - 方案2的无效查询问题:放弃实时监听表单值,改为仅在提交时更新有效参数,不会在用户输入过程中发起无效请求。
- 方案3的缓存自动更新问题:查询只依赖
validQuery,表单临时修改不会触发查询,只有提交后的有效值才会触发更新。 - 方案4的请求规范问题:坚持使用
useQuery,完全符合GET请求用于数据查询的设计原则。
内容的提问来源于stack exchange,提问作者Tobi Kremer
相关产品推荐
相关产品推荐

