引入useQuery后shadcn/ui对话框自动关闭,请求无限循环求助
问题分析与解决:React Query 导致对话框异常关闭及无限请求
问题重现
你编写的useBanks Hook代码:
import { fetchBanks } from "@/api/banks"; import { useQuery } from "react-query"; export const useBanks = () => { return useQuery({ queryKey: ["banks"], queryFn: fetchBanks }); };
在EditBankModal组件中使用该Hook后,出现两个问题:
- 对话框打开后立即关闭
- 网络面板出现无限循环请求
EditBankModal原代码:
import { editBank } from "@/api/banks"; import { Bank } from "@/types/GlobalTypes"; import { AxiosError } from "axios"; import { SubmitHandler, useForm } from "react-hook-form"; import toast from "react-hot-toast"; import { Button } from "../ui/button"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "../ui/dialog"; import { Input } from "../ui/input"; import { useBanks } from "@/hooks/useBanks"; export const EditBankModal = ({ bank }: { bank: Bank }) => { const form = useForm<Bank>({ defaultValues: { ...bank } }); const { handleSubmit, register } = form; const { refetch } = useBanks(); const onSubmit: SubmitHandler<Bank> = async (data) => { await toast.promise(editBank(data), { error: (err: AxiosError<{ status: string; message: string }>) => ( <b>{err.response?.data.message}</b> ), loading: "editing", success: "success", }); refetch(); }; return ( <Dialog> <DialogTrigger asChild> <Button variant="default">Edit</Button> </DialogTrigger> <DialogContent className="sm:max-w-[425px]"> <DialogHeader> <DialogTitle>Edit {bank.bank_name}</DialogTitle> <DialogDescription> Once you are done click save changes. </DialogDescription> </DialogHeader> <form onSubmit={handleSubmit(onSubmit)}> <div className="my-5"> <p className="text-sm font-semibold">Bank name</p> <Input type="text" placeholder="bank name" {...register("bank_name")} /> <p className="text-sm font-semibold mt-5">Bank code</p> <Input type="number" placeholder="bank code" {...register("bank_code")} /> </div> <DialogFooter> <Button type="submit">Submit</Button> </DialogFooter> </form> </DialogContent> </Dialog> ); };
问题根源
- React Query的
useQuery默认会在组件挂载时自动发起请求,请求完成后更新缓存,触发所有订阅该缓存的组件(包括父组件,比如银行列表组件)重新渲染。 - 父组件重新渲染时,会重新创建
EditBankModal的新实例,原实例的对话框打开状态丢失,导致对话框立即关闭。 - 新的
EditBankModal实例挂载后,useQuery再次发起请求,循环往复形成无限请求。
修复方案
不需要在EditBankModal中订阅银行数据,只需要将刷新列表的能力从父组件传递过来即可:
1. 修改父组件(银行列表组件)
在父组件中调用useBanks获取refetch方法,传递给EditBankModal:
import { useBanks } from "@/hooks/useBanks"; import { EditBankModal } from "./EditBankModal"; export const BankList = () => { const { data: banks, refetch } = useBanks(); return ( <div> {banks?.map((bank) => ( <div key={bank.id}> <span>{bank.bank_name}</span> <EditBankModal bank={bank} onSuccess={refetch} /> </div> ))} </div> ); };
2. 修改EditBankModal组件
移除内部的useBanks调用,接收父组件传递的onSuccess回调:
import { editBank } from "@/api/banks"; import { Bank } from "@/types/GlobalTypes"; import { AxiosError } from "axios"; import { SubmitHandler, useForm } from "react-hook-form"; import toast from "react-hot-toast"; import { Button } from "../ui/button"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "../ui/dialog"; import { Input } from "../ui/input"; export const EditBankModal = ({ bank, onSuccess }: { bank: Bank; onSuccess: () => void }) => { const form = useForm<Bank>({ defaultValues: { ...bank } }); const { handleSubmit, register } = form; const onSubmit: SubmitHandler<Bank> = async (data) => { await toast.promise(editBank(data), { error: (err: AxiosError<{ status: string; message: string }>) => ( <b>{err.response?.data.message}</b> ), loading: "editing", success: "success", }); onSuccess(); }; return ( <Dialog> <DialogTrigger asChild> <Button variant="default">Edit</Button> </DialogTrigger> <DialogContent className="sm:max-w-[425px]"> <DialogHeader> <DialogTitle>Edit {bank.bank_name}</DialogTitle> <DialogDescription> Once you are done click save changes. </DialogDescription> </DialogHeader> <form onSubmit={handleSubmit(onSubmit)}> <div className="my-5"> <p className="text-sm font-semibold">Bank name</p> <Input type="text" placeholder="bank name" {...register("bank_name")} /> <p className="text-sm font-semibold mt-5">Bank code</p> <Input type="number" placeholder="bank code" {...register("bank_code")} /> </div> <DialogFooter> <Button type="submit">Submit</Button> </DialogFooter> </form> </DialogContent> </Dialog> ); };
核心逻辑说明
EditBankModal仅负责编辑操作,不需要订阅银行列表数据,避免不必要的组件渲染。- 将刷新逻辑上移到父组件,既能完成数据更新后的列表刷新,又能保持对话框的状态稳定。
内容的提问来源于stack exchange,提问作者Amos Machora
相关产品推荐
相关产品推荐

