Queries
Standard queries, infinite queries, and cache operations.
Standard Queries
Use the query() method to create type-safe query configurations:
class ProjectsClient extends QueryClient<"projects"> {
getProjects(userId: string) {
return this.query({
key: ["list", userId],
fn: async () => {
const response = await fetch(`/api/projects?userId=${userId}`);
return response.json();
},
staleTime: 2 * 60 * 1000, // Override default
});
}
getProject(projectId: string) {
return this.query({
key: ["byId", projectId],
enabled: projectId !== "",
fn: async () => {
const response = await fetch(`/api/projects/${projectId}`);
return response.json();
},
});
}
}Using in Components
import { useQuery } from "@tanstack/react-query";
function ProjectsList({ userId }) {
const projectsClient = useProjectsClient();
const { data, isLoading, error } = useQuery(projectsClient.getProjects(userId));
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<ul>
{data?.map(project => (
<li key={project.id}>{project.name}</li>
))}
</ul>
);
}Cache Operations
Each query result includes utility methods for cache management:
const projectsQuery = projectsClient.getProjects(userId);
// One-time fetch (doesn't subscribe to updates)
const data = await projectsQuery.fetch();
// Populate cache proactively
await projectsQuery.prefetch();
// Read current cached value synchronously
const cached = projectsQuery.get();
// Update cache directly
projectsQuery.set(newData);
// Invalidate and trigger refetch
await projectsQuery.refresh();Infinite Queries
For paginated or infinite-scroll data, use infiniteQuery():
class ProjectsClient extends QueryClient<"projects"> {
getProjectsInfinite() {
return this.infiniteQuery({
key: ["list", "infinite"],
fn: async ({ pageParam }) => {
const response = await fetch(`/api/projects?page=${pageParam}`);
return response.json();
},
initialPageParam: 1,
getNextPageParam: (lastPage, _allPages, lastPageParam) => {
return lastPage.hasMore ? lastPageParam + 1 : undefined;
},
});
}
}Using Infinite Queries
import { useInfiniteQuery } from "@tanstack/react-query";
function InfiniteProjectsList() {
const projectsClient = useProjectsClient();
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
useInfiniteQuery(projectsClient.getProjectsInfinite());
return (
<div>
{data?.pages.map(page =>
page.projects.map(project => (
<div key={project.id}>{project.name}</div>
))
)}
{hasNextPage && (
<button
onClick={() => fetchNextPage()}
disabled={isFetchingNextPage}
>
{isFetchingNextPage ? "Loading..." : "Load More"}
</button>
)}
</div>
);
}Infinite Query Result
interface CreateInfiniteQueryResult {
queryKey: QueryKey;
// ... other InfiniteQueryOptions
fetch(): Promise<InfiniteData<TQueryFnData, TPageParam>>;
refresh(): Promise<void>;
}Query Options
All standard TanStack Query options are supported:
this.query({
key: ["list", userId],
fn: fetchProjects,
// Common options
enabled: userId !== "", // Conditional fetching
staleTime: 5 * 60 * 1000, // Fresh for 5 minutes
gcTime: 30 * 60 * 1000, // Keep unused 30 minutes
refetchOnWindowFocus: true, // Refetch on focus
refetchOnReconnect: true, // Refetch on network reconnect
retry: 3, // Retry failed requests
retryDelay: (attempt) => Math.min(1000 * 2 ** attempt, 30000),
});Conditional Queries
Use enabled to conditionally run queries:
getProject(projectId: string) {
return this.query({
key: ["byId", projectId],
enabled: projectId !== "", // Only fetch when ID is provided
fn: async () => {
const response = await fetch(`/api/projects/${projectId}`);
return response.json();
},
});
}Query Dependencies
Chain dependent queries by using data from one as args for another:
function ProjectDetails({ projectId }) {
const queryClients = useQueryClients();
// First query
const { data: project } = useQuery(
queryClients.projects.getProject(projectId)
);
// Dependent query - only runs when project exists
const { data: owner } = useQuery({
...queryClients.users.getUser(project?.ownerId ?? ""),
enabled: !!project?.ownerId,
});
return (
<div>
<h1>{project?.name}</h1>
<p>Owner: {owner?.name}</p>
</div>
);
}