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

引入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后,出现两个问题:

  1. 对话框打开后立即关闭
  2. 网络面板出现无限循环请求

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 01:50:33