React-query如何判断重新请求(refetch)已完成?
使用React Query的useQuery获取数据并设置表单初始值,需求是仅在首次挂载获取数据后,以及每次refetch完成后更新表单的初始值。当前遇到的问题是isRefetching仅能标识请求正在进行,无法判断refetch是否完成,不知道何时触发表单初始值的更新。
原始实现代码:
父组件:
const { data: caseData, refetch, isRefetching } = useQuery({ queryKey: `caseData-${caseId}`, queryFn: () => fetchCaseById(caseId), staleTime: Infinity, }); if (!caseData) return "Loading" <CaseForm initialValues={createInitialValues(caseData)} refetch={refetch}/>
CaseForm组件内的刷新按钮:
<Button type="button" loading={isRefetching} variant="secondary" onClick={() => refetch()} className="w-max" size="small" > Reload </Button>
尝试过将表单值提升到Context,但仅解决了首次加载的问题,无法感知refetch完成。
方法1:利用React Query的data变化自动触发更新(无需Context)
React Query的data会在每次请求(包括refetch)成功完成后更新,我们可以直接在父组件中监听caseData的变化,将处理后的初始值传递给表单,同时在表单内部通过useEffect监听initialValues的变化来重置表单。
父组件调整
const { data: caseData, refetch, isRefetching } = useQuery({ queryKey: `caseData-${caseId}`, queryFn: () => fetchCaseById(caseId), staleTime: Infinity, }); if (!caseData) return "Loading" const formInitialValues = createInitialValues(caseData); <CaseForm initialValues={formInitialValues} refetch={refetch} isRefetching={isRefetching} />
CaseForm组件调整
使用react-hook-form的reset方法,在initialValues变化时重置表单:
import { useForm } from "react-hook-form"; const CaseForm = ({ initialValues, refetch, isRefetching }) => { const { handleSubmit, control, reset, formState: { errors }, } = useForm({ defaultValues: initialValues, }); // 监听initialValues变化,每次更新时重置表单 useEffect(() => { reset(initialValues); }, [initialValues, reset]); return ( <> {/* 表单内容 */} <Button type="button" loading={isRefetching} variant="secondary" onClick={() => refetch()} className="w-max" size="small" > Reload </Button> </> ); };
这种方式下,每次caseData更新(包括首次加载和refetch完成),父组件都会重新计算formInitialValues,传递给子组件后,子组件通过useEffect触发reset,自动更新表单的初始值。
方法2:结合Context的实现方案
如果必须使用Context管理表单值,可以在父组件中监听caseData的变化,直接更新Context中的值,而不是依赖子组件的判断:
父组件调整
const { caseFormValues, setCaseFormValues } = useCaseProvider(); const { data: caseData, refetch, isRefetching } = useQuery({ queryKey: `caseData-${caseId}`, queryFn: () => fetchCaseById(caseId), staleTime: Infinity, }); if (!caseData) return "Loading" // 首次加载或每次caseData更新时,更新Context中的表单值 useEffect(() => { const formValues = createInitialValues(caseData); setCaseFormValues(formValues); }, [caseData, setCaseFormValues]); <Suspense fallback="Loading"> <CaseForm refetch={refetch} isRefetching={isRefetching}/> </Suspense>
CaseForm组件调整
const { caseFormValues } = useCaseProvider(); const { handleSubmit, control, reset, formState: { errors }, } = useForm({ defaultValues: caseFormValues, }); // 确保Context值更新时重置表单 useEffect(() => { if (caseFormValues) { reset(caseFormValues); } }, [caseFormValues, reset]); // 按钮部分不变
这里的核心是直接监听caseData的变化,因为React Query的data只有在请求成功完成后才会更新,不管是首次加载还是refetch,只要请求成功,caseData就会更新,此时我们就可以触发Context值的更新,进而让表单重置。
关键原理
React Query的data状态是请求成功完成后才会更新的,无论是首次查询还是refetch触发的重新查询,只要请求成功,data就会变成最新值。所以不需要依赖isRefetching来判断完成状态,直接监听data的变化即可准确捕获“数据更新完成”的时机。
内容的提问来源于stack exchange,提问作者Leff

