DEV Community

Alex Spinov
Alex Spinov

Posted on

React Query (TanStack Query) Has a Free API — Here's How to Manage Server State

TanStack Query (formerly React Query) is the gold standard for server state management. It handles caching, background refetching, pagination, optimistic updates, and more.

Installation

npm install @tanstack/react-query
Enter fullscreen mode Exit fullscreen mode

Setup

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

const queryClient = new QueryClient({
  defaultOptions: {
    queries: { staleTime: 5 * 60 * 1000, retry: 3 }
  }
});

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <MyApp />
    </QueryClientProvider>
  );
}
Enter fullscreen mode Exit fullscreen mode

useQuery — Fetch Data

import { useQuery } from "@tanstack/react-query";

function Posts() {
  const { data, isLoading, error } = useQuery({
    queryKey: ["posts"],
    queryFn: async () => {
      const res = await fetch("/api/posts");
      if (!res.ok) throw new Error("Failed to fetch");
      return res.json();
    }
  });

  if (isLoading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <ul>
      {data.map(post => <li key={post.id}>{post.title}</li>)}
    </ul>
  );
}
Enter fullscreen mode Exit fullscreen mode

useMutation — Modify Data

import { useMutation, useQueryClient } from "@tanstack/react-query";

function CreatePost() {
  const queryClient = useQueryClient();

  const mutation = useMutation({
    mutationFn: async (newPost) => {
      const res = await fetch("/api/posts", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(newPost)
      });
      return res.json();
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ["posts"] });
    }
  });

  return (
    <button onClick={() => mutation.mutate({ title: "New Post", content: "Hello!" })}>
      {mutation.isPending ? "Creating..." : "Create Post"}
    </button>
  );
}
Enter fullscreen mode Exit fullscreen mode

Dependent Queries

function UserPosts({ userId }) {
  const user = useQuery({ queryKey: ["user", userId], queryFn: () => fetchUser(userId) });
  const posts = useQuery({
    queryKey: ["posts", userId],
    queryFn: () => fetchPostsByUser(userId),
    enabled: !!user.data  // Only fetch when user is loaded
  });
}
Enter fullscreen mode Exit fullscreen mode

Infinite Scroll

import { useInfiniteQuery } from "@tanstack/react-query";

function InfinitePosts() {
  const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useInfiniteQuery({
    queryKey: ["posts"],
    queryFn: ({ pageParam = 0 }) => fetch(`/api/posts?offset=${pageParam}`).then(r => r.json()),
    getNextPageParam: (lastPage, pages) => lastPage.nextOffset ?? undefined,
    initialPageParam: 0
  });

  return (
    <div>
      {data?.pages.flatMap(page => page.posts).map(post => (
        <div key={post.id}>{post.title}</div>
      ))}
      {hasNextPage && (
        <button onClick={() => fetchNextPage()} disabled={isFetchingNextPage}>
          {isFetchingNextPage ? "Loading..." : "Load More"}
        </button>
      )}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Need to extract or automate web content at scale? Check out my web scraping tools on Apify — no coding required. Or email me at spinov001@gmail.com for custom solutions.

Top comments (0)