useQueryClient 全面指南:获取当前 QueryClient 实例的 Hook 及实践用法)
TanStack QueryReact QueryuseQueryClient 全面指南获取当前 QueryClient 实例的 Hook 及实践用法【免费下载链接】query Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.项目地址: https://gitcode.com/GitHub_Trending/qu/query在 TanStack Query 的 React 适配层中useQueryClient是获取「当前QueryClient实例」的核心 Hook。本指南聚焦于该 Hook 的完整 API、底层实现原理Context 读取与错误抛出机制并结合仓库源码与测试用例深入讲解它在 mutation 失效、乐观更新、预取与手动读取缓存等真实场景下的实战用法。读完本文你将理解useQueryClient与QueryClientProvider的关系并能在任何组件中安全、正确地拿到并操控全局查询客户端。说明本文对应的原始 API 参考文档位于 docs/framework/react/reference/functions/useQueryClient.md属于框架参考Reference系列中函数级 API 文档的一份正文全部论点均有当前仓库源码/测试/文档佐证。一、API 一览签名、参数、返回值与异常useQueryClient的完整 TypeScript 签名如下function useQueryClient(queryClient?): QueryClient;围绕这份签名官方参考文档明确了三个关键约定1.1 参数queryClient?可选类型QueryClient。语义传入时Hook直接返回你传入的这个自定义QueryClient不再从 Context 中读取。语义不传时返回最近一层 Context中提供的QueryClient。也就是说绝大多数场景下你根本不需要传参——只要组件树上层存在QueryClientProvideruseQueryClient()就会拿到它。1.2 返回值返回类型为QueryClient即「当前QueryClient实例」。拿到该实例后即可在组件内调用其命令式 API见本文第三节。1.3 抛错Throws行为官方文档明确当既没有传入queryClient参数、又在组件树中找不到QueryClientProvider时useQueryClient会抛出异常。二、源码级拆解useQueryClient 到底做了什么Hook 的实现在 packages/react-query/src/QueryClientProvider.tsx#L21-L33逻辑非常精简核心就是「React Context 读取 防御性兜底」export const QueryClientContext React.createContextQueryClient | undefined( undefined, ) export const useQueryClient (queryClient?: QueryClient) { const client React.useContext(QueryClientContext) if (queryClient) { return queryClient } if (!client) { throw new Error(No QueryClient set, use QueryClientProvider to set one) } return client }从源码结构可以提炼出三条底层事实数据源是一个 React ContextQueryClientContext在 packages/react-query/src/QueryClientProvider.tsx#L9-L11 通过React.createContextQueryClient | undefined(undefined)创建默认值为undefined。该 Context 同样作为独立变量导出参见 QueryClientContext 参考文档。参数优先于 Context一旦传入queryClientHook 立即返回该实例连 Context 的取值结果都会被忽略这是覆盖默认实例的逃生舱口。兜底抛错的信息即测试断言的原文当 Context 中取不到 client 时会抛出No QueryClient set, use QueryClientProvider to set one这一行为在 packages/react-query/src/tests/QueryClientProvider.test.tsx#L146-L163 中被原样断言expect(() render(Page /)).toThrow(...)说明「无 Provider 即抛错」是被测试保证的稳定契约。2.1 谁负责往 Context 里塞 QueryClientQueryClientProvider往QueryClientContext写入值的正是QueryClientProvider组件它与useQueryClient定义在同一个文件 packages/react-query/src/QueryClientProvider.tsx#L70-L86export const QueryClientProvider ({ client, children, }: QueryClientProviderProps): React.JSX.Element { React.useEffect(() { client.mount() return () { client.unmount() } }, [client]) return ( QueryClientContext.Provider value{client} {children} /QueryClientContext.Provider ) }从该实现可以看到两件值得注意的事client为必填 propchildren为可选 propQueryClientProviderProps类型声明同样位于本文件packages/react-query/src/QueryClientProvider.tsx#L38-L49。Provider 挂载/卸载时分别调用client.mount()与client.unmount()使客户端订阅窗口 focus / 网络 online 事件当应用重新获得焦点或恢复联网时能够恢复被暂停的 mutation 并按需重新拉取数据详细说明参见 QueryClientProvider 参考文档。因此「QueryClientProvider 负责注入、useQueryClient 负责读取」是这套机制的最小闭环。2.2 使用方不止你库内部 Hook 也在调用它useQueryClient不只是一个开放给用户的功能React Query 自身的多数 Hook 也依赖它定位客户端。检索 packages/react-query/src 可以发现调用方包括useBaseQuery.ts、useQuery.ts、useMutation.ts、useQueries.ts、useIsFetching.ts、useMutationState.ts、usePrefetchQuery.tsx、usePrefetchInfiniteQuery.tsx与HydrationBoundary.tsx等。例如 packages/react-query/src/useMutation.ts#L9 顶部直接import { useQueryClient } from ./QueryClientProvider从而在 mutation 成功回调里拿到同一个 client。这也解释了一个通用契约useQuery、useMutation、useQueries等 Hook 的第二个可选参数同样是queryClient——当你不传时它们内部走的正是「用useQueryClient()从最近 Context 取默认实例」这条路见 useQuery 参考文档 中各重载对queryClient参数的说明。公共导出统一在 packages/react-query/src/index.ts其中第 33-37 行将QueryClientContext、QueryClientProvider、useQueryClient一并导出import { QueryClientContext, QueryClientProvider, useQueryClient, } from ./QueryClientProvider三、从入门到实战useQueryClient 的典型用法3.1 最小可用骨架先 Provide再 useQueryClientuseQueryClient能否工作完全取决于上层有没有QueryClientProvider。标准结构如下import { QueryClient, QueryClientProvider } from tanstack/react-query const queryClient new QueryClient() function App() { return ( QueryClientProvider client{queryClient} MyPage / /QueryClientProvider ) }在MyPageProvider 子树内的任意组件里即可通过useQueryClient()取到同一个实例。若漏掉 Provider组件一渲染就会抛出No QueryClient set, use QueryClientProvider to set one——这也是排查「useQueryClient 突然报错」时的第一排查点。3.2 实战一mutation 成功后使相关查询失效最常见的需求写操作结束后让受影响的查询重新拉取。通过useQueryClient取得 client 后调用invalidateQueries即可完整范例见 useMutation 参考文档import { useMutation, useQueryClient } from tanstack/react-query function AddTodo() { const queryClient useQueryClient() const addMutation useMutation({ mutationFn: addTodo, onSuccess: () queryClient.invalidateQueries({ queryKey: [todos] }), }) return ( button onClick{() addMutation.mutate(Item)}Add/button ) }invalidateQueries支持精确、前缀与模糊fuzzy多种匹配其行为细节参见核心参考 docs/reference/QueryClient.md#L211。3.3 实战二乐观更新 失败回滚乐观更新通常需要依次调用cancelQueries取消进行中的请求、getQueryData备份旧值、setQueryData写入乐观值失败时再setQueryData恢复备份——这些都属于QueryClient的命令式 APIimport { useMutation, useQueryClient } from tanstack/react-query function AddTodo() { const queryClient useQueryClient() const addMutation useMutation({ mutationFn: addTodo, onMutate: async (newTodo) { await queryClient.cancelQueries({ queryKey: [todos] }) const previousTodos queryClient.getQueryDataArraystring([todos]) queryClient.setQueryDataArraystring([todos], (old) [ ...(old ?? []), newTodo, ]) // 传给 onError 作为第三个参数用于回滚 return { previousTodos } }, onError: (_err, _newTodo, onMutateResult) { queryClient.setQueryData([todos], onMutateResult?.previousTodos) }, onSettled: () { queryClient.invalidateQueries({ queryKey: [todos] }) }, }) return button onClick{() addMutation.mutate(Item)}Add/button }3.4 实战三用缓存数据给详情查询播种 initialData当列表数据已在缓存中详情页可以先从缓存取出对应条目作为initialData跳过加载态直接展示import { useQuery, useQueryClient } from tanstack/react-query function Post({ postId }: { postId: number }) { const queryClient useQueryClient() const { data, isError, error } useQuery({ queryKey: [post, postId], queryFn: () fetchPost(postId), initialData: () queryClient .getQueryDataArrayPost([posts]) ?.find((post) post.id postId), }) if (isError) return spanError: {error.message}/span return h1{data?.title}/h1 }该示例出自 packages/react-query/src/useQuery.ts#L225-L246 中useQuery的文档注释属于官方推荐的「以缓存的列表数据初始化详情查询」模式。其余类似命令式 APIsetQueriesData、getQueryState、refetchQueries、ensureQueryData、prefetchQuery等的完整清单可查阅 docs/reference/QueryClient.md。3.5 进阶传入自定义 QueryClient绕过 Context当一个组件需要无视上层 Provider、强制使用某个特定客户端时可把实例作为第一个参数传入import { useQueryClient } from tanstack/react-query const customClient new QueryClient() function SpecialComponent() { // 直接返回 customClient根本不读 Context const client useQueryClient(customClient) // ... }需要说明的是这种用法一般用于库作者封装、测试隔离或组件需操作独立客户端的少见场景同时传参的模式也被 React Query 自身的 Hook 采用如useQuery(options, queryClient)以保证「显式传入时优先于 Context」这一规则在整个 API 面的一致。四、内部 Hook 同款签名其他框架适配层的行为一致useQueryClient并非 React 专属。在当前仓库的其它框架包中同名 Hook 遵循完全一致的「可选参数 最近 Context」约定packages/vue-query/src/useQueryClient.ts 及其测试 packages/vue-query/src/tests/useQueryClient.test.ts、文档 docs/framework/vue/reference/useQueryClient.mdLit 与 Preact 的参考文档 docs/framework/lit/reference/functions/useQueryClient.md、docs/framework/preact/reference/functions/useQueryClient.mdSvelte 的实现 packages/svelte-query/src/useQueryClient.ts。因此本文总结的「先由 Provider 注入、再读取默认实例、可显式覆盖、无 Provider 即抛错」四条规则在 TanStack Query 各框架绑定中具有普遍适用性。五、常见坑位与最佳实践小结无 Provider 抛错是设计而非缺陷错误信息No QueryClient set, use QueryClientProvider to set one由源码直接抛出并被测试锁定提示信息本身就在告诉你修复方向——检查组件树上方是否遗漏QueryClientProvider。Provider 的 mount/unmount 副作用很重要QueryClientProvider挂载/卸载会触发client.mount()/client.unmount()保证窗口 focus/网络恢复时的自动刷新与暂停 mutation 恢复自行创建 Provider 时不要破坏这一生命周期。默认取最近一层 Provider支持嵌套多个QueryClientProvider实现多缓存分区。参考测试 packages/react-query/src/tests/QueryClientProvider.test.tsx#L52-L106两个 Provider 各自携带独立QueryCache时queryCache1中找不到属于queryCache2的 key反之亦然——子组件永远拿到「最近的」那个 client。配合 SSR/水合使用HydrationBoundary内部也读取useQueryClient()来定位客户端并注入脱水的查询数据见 HydrationBoundary 参考文档理解这一点有助于排查 SSR 场景下「数据已脱水却无法水合」的问题。把useQueryClient与 QueryClientProvider 参考文档、QueryClient 核心参考、useMutation 参考文档、useQuery 参考文档 放在一起阅读即可掌握 TanStack Query 在 React 中「实例注入—读取—命令式操控」的完整链路。【免费下载链接】query Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.项目地址: https://gitcode.com/GitHub_Trending/qu/query创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考