Because TanStack Query uses a global cache, you can use the same useQuery hook in multiple places without making excess XHR requests.
Traditionally, if you have data on a server, that you want to use in multiple places throughout your component tree, the advice is to lift it to the top and either prop-drill its data or put it in a context. In this contrived example, suppose the <Header> component and the <Dashboard> component both need the data from the primary server fetch. And the <Footer> component needs to know if there was an error fetching that server data:
function App() {
const query = useQuery({
queryKey: ["data"],
queryFn: () => fetchData()
})
return <div>
<Header data={query.data}/>
<Dashboard data={query.data} isPending={query.isPending}/>
<Footer error={query.error}/>
</div>
}
But prop-drilling can be annoying. For example, that <Footer> component might look like this:
function Footer({error}: {error: Error | null}) {
return <footer>
<Alert error={error}/>
<p>Copyright 2026</p>
</footer>
}
function Alert({error}: {error: Error | null}) {
if (error) {
return <div className="footer-error">There was an error fetching the data</div>
}
return null
}
What you can do instead
First, refactor the useQuery call so it becomes a hook that encapsulates any of its configuration. For example:
function useData() {
return useQuery({
queryKey: ["data"],
queryFn: () => fetchData(),
retry: 0,
});
}
Now, you can use that hook in all the places where it's needed. For example:
function Header() {
const {data} = useData();
return <h1>
Page Title {data ? ` (${data.count} items)` : null}
</h1>
}
function Footer() {
return <footer>
<Alert/>
<p>Copyright 2026</p>
</footer>
}
function Alert() {
const {error} = useData()
if (error) {
return <div className="footer-error">There was an error fetching the data</div>
}
return null
}
And in the end, you can remove any drop-drilling
function App() {
return <div>
<Header/>
<Dashboard/>
<Footer/>
</div>
}
Conclusion
The useQuery hook will use the global cache, that probably looks something like this:
import { App } from "./App";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
const queryClient = new QueryClient();
const app = (
<StrictMode>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</StrictMode>
);
And that global cache will recognize if a query has started by the queryKey value. If one such Promise already exists, it won't start another. So you're guaranteed to not make multiple XHR queries to the back end.
Comments