kb.erickguedes.com
TanStack: Ecossistema de Bibliotecas

TanStack Query: Gerenciamento de Server State

Aula 1 de 5

Fundamentos do TanStack Query

O TanStack Query (anteriormente React Query) é a biblioteca mais popular para gerenciamento de estado assíncrono no ecossistema React. Ela abstrai fetching, caching, sincronização e atualizações de dados do servidor.

QueryClient e Provider

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

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60 * 5, // 5 minutos
      gcTime: 1000 * 60 * 30, // 30 minutos (antes cacheTime)
      retry: 2,
      refetchOnWindowFocus: true,
    },
  },
})

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <RouterProvider router={router} />
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  )
}

useQuery — Consultas Básicas

import { useQuery } from '@tanstack/react-query'

function UserProfile({ userId }: { userId: string }) {
  const { data, isLoading, error, isError } = useQuery({
    queryKey: ['users', userId],
    queryFn: async () => {
      const res = await fetch(`/api/users/${userId}`)
      if (!res.ok) throw new Error('Falha ao carregar')
      return res.json()
    },
    staleTime: 1000 * 60 * 2,
  })

  if (isLoading) return <Spinner />
  if (isError) return <Error message={error.message} />

  return <div>{data.name}</div>
}

useMutation — Mutações e Invalidação

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

function CreateUser() {
  const queryClient = useQueryClient()

  const mutation = useMutation({
    mutationFn: (newUser: UserData) =>
      fetch('/api/users', {
        method: 'POST',
        body: JSON.stringify(newUser),
        headers: { 'Content-Type': 'application/json' },
      }).then(r => r.json()),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['users'] })
    },
  })

  return (
    <button onClick={() => mutation.mutate({ name: 'João' })} disabled={mutation.isPending}>
      {mutation.isPending ? 'Criando...' : 'Criar Usuário'}
    </button>
  )
}

Chaves de Query e Dependências

// Query key como dependência
function UserList({ page, search }: { page: number; search: string }) {
  return useQuery({
    queryKey: ['users', { page, search }],
    queryFn: () => fetchUsers({ page, search }),
    placeholderData: keepPreviousData, // mantém dados anteriores durante loading
  })
}

Paginação e Infinite Queries

import { useInfiniteQuery } from '@tanstack/react-query'

function Feed() {
  const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useInfiniteQuery({
    queryKey: ['feed'],
    queryFn: ({ pageParam }) => fetchFeed(pageParam),
    initialPageParam: 0,
    getNextPageParam: (lastPage) => lastPage.nextCursor ?? undefined,
  })

  return (
    <div>
      {data?.pages.map(page =>
        page.items.map(item => <FeedItem key={item.id} item={item} />)
      )}
      <button onClick={() => fetchNextPage()} disabled={!hasNextPage}>
        {isFetchingNextPage ? 'Carregando...' : 'Carregar mais'}
      </button>
    </div>
  )
}

Optimistic Updates

const mutation = useMutation({
  mutationFn: updateTodo,
  onMutate: async (newTodo) => {
    await queryClient.cancelQueries({ queryKey: ['todos'] })
    const previous = queryClient.getQueryData(['todos'])
    queryClient.setQueryData(['todos'], (old: Todo[]) =>
      old.map(t => t.id === newTodo.id ? { ...t, ...newTodo } : t)
    )
    return { previous }
  },
  onError: (err, newTodo, context) => {
    queryClient.setQueryData(['todos'], context?.previous)
  },
  onSettled: () => {
    queryClient.invalidateQueries({ queryKey: ['todos'] })
  },
})

Lab: Aplicação de Dashboard

npm create vite@latest query-demo -- --template react-ts
cd query-demo
npm install @tanstack/react-query @tanstack/react-query-devtools
// Crie um dashboard com:
// 1. useQuery para listar posts de /api/posts
// 2. useMutation para criar post com optimistic update
// 3. Invalidate queries após mutação
// 4. ReactQueryDevtools para depuração

O TanStack Query elimina a necessidade de gerenciar loading/error/cache manualmente, centralizando o estado do servidor em uma camada previsível e performática.