$devvkit learn --librarie tanstack-query-(react-query)-guide
TanStack Query (React Query) Guide
[react][data-fetching][caching]
JavaScript / TypeScript
Install
npm install @tanstack/react-query
TanStack Query solves keeping server state in sync with the UI. It handles caching, background refetching, and optimistic updates.
Server state is fundamentally different from client state. TanStack Query treats server state as a cache, not source of truth.
v5 uses useQuery for reads, useMutation for writes, and queryClient.invalidateQueries to refetch.
Setup
Provider— Wrap app with QueryClientProvider.
const queryClient = new QueryClient();
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>Queries
Basic query— Fetch data.
const { data, isLoading } = useQuery({
queryKey: ['users'],
queryFn: () => fetch('/api/users').then(r => r.json()),
});Query with params— Depend on variable.
const { data } = useQuery({
queryKey: ['users', userId],
queryFn: () => fetch('/api/users/' + userId).then(r => r.json()),
});Enabled— Skip until condition.
useQuery({ queryKey: ['users', userId], queryFn: fetchUser, enabled: !!userId });Mutations
Mutation— Create and invalidate.
const mutation = useMutation({
mutationFn: (newUser) => fetch('/api/users', { method: 'POST', body: JSON.stringify(newUser) }),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['users'] }),
});Optimistic update— Update UI before server.
useMutation({
mutationFn: updateTodo,
onMutate: async (newTodo) => {
await queryClient.cancelQueries({ queryKey: ['todos'] });
const prev = queryClient.getQueryData(['todos']);
queryClient.setQueryData(['todos'], (old) => [...old, newTodo]);
return { prev };
},
onError: (err, newTodo, context) => queryClient.setQueryData(['todos'], context.prev),
onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
});Caching
Stale time— Control freshness duration.
useQuery({ queryKey: ['users'], queryFn: fetchUsers, staleTime: 5 * 60 * 1000, gcTime: 30 * 60 * 1000 });Pagination
Infinite query— Load more pattern.
const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
queryKey: ['projects'],
queryFn: ({ pageParam }) => fetch(/api/projects?cursor=).then(r => r.json()),
initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.nextCursor ?? null,
});