This is a follow-up to useQuery.
When several components need the same data, fetching it separately in each component can repeat work. Data fetching libraries such as SWR, TanStack Query, and fate share cached data across components, although their cache models and refresh policies differ.
Implement a useQuery hook that shares pending requests and caches successful results across hook instances.
Use useQuery(queryKey, fn), where queryKey is a string such as "user:42". The key replaces the dependency array from the first question and identifies which data to fetch or reuse. When the key changes, the hook reuses the pending request or successful result cached for that key, or calls fn() if the key is missing from the cache.
function UserName({ userId }) {const request = useQuery(`user-name:${userId}`, async () => {const response = await fetch(`/api/users/${userId}`);const user = await response.json();return user.name;});if (request.status === 'loading') {return <p>Loading...</p>;}if (request.status === 'error') {return <p>Could not load the user.</p>;}return <p>{request.data}</p>;}export default function App() {return (<><UserName userId="42" /><UserName userId="42" /></>);}
Both components use the key "user-name:42", so only one fetch should start. Both receive its result. Mounting another component with that key after success should reuse the cached result.
queryKey: string: The cache key. Include every input that changes the requested data. Equal strings identify the same query; different strings identify independent queries.fn: () => Promise<T>: A function that produces the data. Callers using the same key must request interchangeable data of the same type. The first caller's function starts a missing query.Changing queryKey selects the corresponding cached or new request. Changing only the identity of fn must not start another request.
The hook returns one of these objects:
{ status: 'loading' } while waiting for the selected request.{ status: 'success', data: T } when it fulfills.{ status: 'error', error: unknown } when it rejects or fn throws.Starting to observe a different query resets the state to { status: 'loading' }, clearing previous data or errors. Cached results may be delivered asynchronously.
fn() once and immediately store the pending promise in the cache. Other hook instances requesting that key while the promise is pending must share it without calling fn() again.