使用React-Query useMutation与react-hook-form提交数据时遇无限循环错误
解决React Hook Form + React Query useMutation无限重渲染问题
问题根源
你遇到的无限重渲染循环,核心原因是**useMutation的配置对象每次渲染都会生成新实例**,加上reset()触发的组件重渲染,形成了循环:
- 组件调用
useCreateTag时,每次都会创建新的mutationFn和onSuccess函数,导致useMutation重新初始化,返回新的mutation对象。 mutate引用变化后,依赖mutate的onSubmit(通过useCallback)会重新创建。- 表单的
onSubmit属性更新,触发组件重渲染,回到第一步循环。 - 当
mutation成功调用reset()后,表单状态变化进一步触发重渲染,彻底激活这个循环。
另外,如果reset函数的引用不稳定(比如useForm的配置依赖了每次渲染都变化的值),会加剧这个问题。
解决方案
方案一:缓存useMutation的配置对象
在useCreateTag中用useMemo包裹useMutation的配置,确保只有依赖变化时才重新生成配置:
import { useMemo } from 'react'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import axios from 'axios'; import { Routes } from '../your-routes-path'; import { Tag } from '../your-tag-type-path'; const createTag = async (values: Tag, tenantId: string) => { const body = { ...values, tenantId, }; const { data } = await axios.post(Routes.API.TAGS, body); return data; }; export const useCreateTag = (tenantId: string, reset: () => void) => { const queryClient = useQueryClient(); const mutationConfig = useMemo(() => ({ mutationFn: (values: Tag) => createTag(values, tenantId), onSuccess: () => { successToast('Tag created'); reset(); queryClient.invalidateQueries({ queryKey: ['tags', tenantId], }); }, }), [tenantId, reset, queryClient]); const mutation = useMutation(mutationConfig); return mutation; };
方案二:将reset逻辑移到组件内部
把reset和提示框逻辑从自定义钩子中移除,放在组件的mutate调用参数里,减少钩子的依赖:
// 组件代码 const { register, handleSubmit, reset } = useForm(); const { mutate, isLoading, isError, error } = useCreateTag(tenantId); const onSubmit: SubmitHandler<Tag> = useCallback( (values: Tag) => { mutate(values, { onSuccess: () => { successToast('Tag created'); reset(); }, }); }, [mutate, reset], ); // 自定义钩子代码 export const useCreateTag = (tenantId: string) => { const queryClient = useQueryClient(); const mutation = useMutation({ mutationFn: (values: Tag) => createTag(values, tenantId), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['tags', tenantId], }); }, }); return mutation; };
方案三:确保reset函数引用稳定
如果reset的引用不稳定,检查useForm的配置,用useMemo缓存defaultValues等依赖项:
const defaultValues = useMemo(() => ({ // 你的表单默认值,依赖props的话在这里处理 }), [props.yourDependency]); const { register, handleSubmit, reset } = useForm({ defaultValues });
这些方案都能打破无限重渲染的循环,推荐优先尝试方案一或方案二,根据你的代码结构选择最合适的。
内容的提问来源于stack exchange,提问作者Tibo
相关产品推荐
相关产品推荐

