为何shadcn/ui DataTable在TanStack Query失效后不重新渲染?
解决Shadcn DataTable删除记录后不更新的问题
问题场景
使用Next.js、TanStack React Query和Shadcn UI开发,包含Substance List、Shadcn DataTable、Shadcn Columns三个组件。执行删除操作后,DataTable无法自动重新渲染以同步数据变化。已在Columns组件中触发删除并调用queryClient.invalidateQueries({ queryKey: ["substances"] }),但表格仍无更新。
核心错误原因
在Columns组件的actions列cell函数中,你新建了一个QueryClient实例:
const queryClient = new QueryClient();
这个实例和Substances组件中useQuery使用的全局QueryClient实例不是同一个,所以调用invalidateQueries只会清空这个局部实例的缓存,根本不会触发全局缓存的刷新,自然无法让Substances组件里的useQuery重新获取数据。
修复步骤
1. 使用全局QueryClient实例
替换new QueryClient()为React Query提供的useQueryClient()钩子,获取应用全局的QueryClient实例。
2. 确保Hook符合规则
保证useQueryClient和useMutation在cell函数的最顶层调用,符合React Hook的使用规则。
修改后的Columns组件代码
"use client"; import { useRouter } from "next/navigation"; import axios from "axios"; import toast from "react-hot-toast"; // 导入useQueryClient钩子,而非QueryClient类 import { useMutation, useQueryClient } from "@tanstack/react-query"; import { ColumnDef } from "@tanstack/react-table"; import { MoreHorizontal } from "lucide-react"; import { Button } from "@/components/ui/button"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import Link from "next/link"; import { Substance } from "@prisma/client"; export const columns: ColumnDef<Substance>[] = [ { accessorKey: "name", header: "名称", cell: ({ row }) => { const substance = row.original; return ( <Link href={`/substances/${substance.id}`} className="underline text-sky-700" > {substance.name} </Link> ); }, }, { accessorKey: "type", header: "类型", }, { accessorKey: "EVcode", header: "EV编码", }, { accessorKey: "status", header: "状态", }, { id: "actions", cell: ({ row }) => { const substance = row.original; const router = useRouter(); // 使用useQueryClient获取全局实例 const queryClient = useQueryClient(); const handleEdit = (substanceId: string) => { router.push(`/substances/${substanceId}`); }; const { mutate: handleDeleteMutation } = useMutation({ mutationFn: async (substanceId: string) => { await axios.delete("/api/substances", { data: { substanceId } }); }, onSuccess: () => { // 现在调用的是全局QueryClient的invalidateQueries queryClient.invalidateQueries({ queryKey: ["substances"] }); toast.success("物质已删除"); }, onError: () => { toast.error("删除失败,请重试"); }, }); return ( <DropdownMenu> <DropdownMenuTrigger asChild> <Button variant="ghost" className="h-8 w-8 p-0"> <span className="sr-only">打开菜单</span> <MoreHorizontal className="h-4 w-4" /> </Button> </DropdownMenuTrigger> <DropdownMenuContent align="end"> <DropdownMenuLabel>操作</DropdownMenuLabel> <DropdownMenuItem onClick={() => handleEdit(substance.id)}> 编辑 </DropdownMenuItem> <DropdownMenuSeparator /> <DropdownMenuItem>复制</DropdownMenuItem> <DropdownMenuItem onClick={() => handleDeleteMutation(substance.id)} > 删除 </DropdownMenuItem> </DropdownMenuContent> </DropdownMenu> ); }, }, ];
额外检查点
- 确保你的Next.js项目已经正确配置了React Query的全局Provider(通常在
layout.tsx中),示例配置:
// app/layout.tsx import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; const queryClient = new QueryClient(); export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html lang="zh-CN"> <body> <QueryClientProvider client={queryClient}> {children} </QueryClientProvider> </body> </html> ); }
- Substances组件中确保
data正确传递给DataTable,并且没有在渲染时做不必要的缓存或深拷贝,导致数据变化无法被检测到。
内容的提问来源于stack exchange,提问作者Aro
相关产品推荐
相关产品推荐

