Establish clear data acquisition, caching, invalidation and submission boundaries for front-end server state to avoid request state being scattered among page components.
tsx
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
export function UserList({ keyword }: { keyword: string }) {
const query = useQuery({
queryKey: ["users", "list", { keyword }],
queryFn: () => getUserList({ keyword, page: 1, pageSize: 20 }),
select: (response) => response.list,
});
if (query.isLoading) return <Skeleton />;
if (query.isError) return <ErrorFallback onRetry={() => query.refetch()} />;
if (!query.data?.length) return <EmptyState />;
return query.data.map((user) => <UserRow key={user.id} user={user} />);
}
export function useCreateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: createUser,
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["users"] }),
});
}
Load references/query-patterns.md when it comes to whether you need a query library, QueryClient default configuration, Vue adapter, optimistic updates, infinite scroll queries, prefetching, SSR hydration, and API layer integration.
staleTime is too long, old data will be displayed, and if it is too short, it will cause frequent requests.The data acquisition layer has loading/error/empty/data status, repeated requests are automatically deduplicated, the cache is correctly invalidated or rolled back after mutation, and the boundary between the API layer and the UI layer is clear.