使用@tanstack/react-query刷新页面时出现错误求助
问题分析与解决方案
这个报错的核心原因是Remix的服务端渲染(SSR)和React Query的客户端初始化逻辑不兼容:
- 你当前代码里的
QueryClient是全局单例,服务端处理多请求时会出现实例复用问题,导致跨请求状态污染。 - 刷新子页面时,服务端先渲染组件,但React Query的
useQuery在服务端默认不会触发请求,服务端渲染的是Loading...状态;而客户端hydrate时如果直接获取到数据,会导致DOM内容不匹配,进而触发innerHTML相关错误。
修正后的代码示例
1. 在Root路由中处理QueryClient与SSR同步
// app/root.tsx import { QueryClient, QueryClientProvider, dehydrate, Hydrate, } from "@tanstack/react-query"; import { Outlet, useLoaderData } from "@remix-run/react"; export async function loader() { // 为每个请求创建独立的QueryClient实例 const queryClient = new QueryClient(); // 服务端预取子页面需要的数据,确保服务端渲染内容与客户端一致 await queryClient.prefetchQuery({ queryKey: ["repoData"], queryFn: () => fetch("https://jsonplaceholder.typicode.com/posts/1").then((res) => res.json()), }); return dehydrate(queryClient); } export default function App() { const dehydratedState = useLoaderData(); // 客户端创建新QueryClient,用服务端的脱水状态初始化 const queryClient = new QueryClient(); return ( <html lang="zh-CN"> <head> <title>Remix + React Query</title> </head> <body> <QueryClientProvider client={queryClient}> <Hydrate state={dehydratedState}> <Outlet /> </Hydrate> </QueryClientProvider> </body> </html> ); }
2. 子页面组件简化(无需再包裹QueryClientProvider)
// app/routes/your-sub-page.tsx import { useQuery } from "@tanstack/react-query"; export default function Device2ServerFrameParser() { return <Example />; } function Example() { const { isLoading, error, data } = useQuery({ queryKey: ["repoData"], queryFn: () => fetch("https://jsonplaceholder.typicode.com/posts/1").then((res) => res.json() ), }); if (isLoading) return <div>Loading...</div>; if (error) return <div>Error</div>; return ( <div> <span>{JSON.stringify(data)}</span> </div> ); }
关键调整点
- 每个请求独立QueryClient:服务端loader内创建实例,避免跨请求状态污染。
- 服务端预取数据:通过
prefetchQuery让服务端提前获取数据,确保服务端渲染内容与客户端hydrate后的内容一致,解决DOM不匹配问题。 - 使用Hydrate组件:将服务端脱水的状态同步到客户端QueryClient,避免客户端重复请求且保证hydration流程正常。
内容的提问来源于stack exchange,提问作者Gi Tobias
相关产品推荐
相关产品推荐

