Server State

TanStack Query

TanStack Query é a camada de busca de dados que faltava para React. Ele transforma dados do servidor em um cache, gerenciando carregamento, erros, refetching e invalidação para você.

intermediate14 min readUpdated 15 de set. de 2026
usePosts.ts
tsx
// usePosts.ts
import { useQuery } from "@tanstack/react-query";

async function fetchPosts() {
  const res = await fetch("/api/posts");
  if (!res.ok) throw new Error("Failed to fetch posts");
  return res.json();
}

export function usePosts() {
  return useQuery({
    queryKey: ["posts"],
    queryFn: fetchPosts,
  });
}
Antigamente
React Query
Framework
React, Vue, Svelte, Solid
Leitura com
useQuery
Escrita com
useMutation
Chave de cache
queryKey
Devtools
Painel dedicado

Por que importa

Por que o server state precisa de sua própria ferramenta

Um cache real para dados do servidor

As requisições são cacheadas, deduplicadas e compartilhadas entre componentes, garantindo que o mesmo dado seja buscado apenas uma vez.

Loading e erro nativos

Cada query expõe status, loading, error e data, eliminando a necessidade de escrever essas flags manualmente.

Atualização por política

Stale time, refetch on focus e atualizações em segundo plano mantêm os dados atuais sem a necessidade de trabalho manual constante.

O panorama completo

As três ideias por trás do TanStack Query

Um cache indexado por query, uma função de query que busca os dados e a invalidação que mantém o cache atualizado.

Queries

Leitura

Uma query key somada a uma função de fetch descreve um pedaço de dado do servidor e sua entrada no cache.

Mutations

Escrita

O useMutation gerencia requisições de criação, atualização e exclusão, podendo atualizar o cache após o sucesso.

Invalidation

Sincronização

Marca dados cacheados como obsoletos (stale) para que sejam buscados novamente, mantendo a UI consistente após uma alteração.

TanStack Query em resumo

O núcleo do TanStack Query

useQuery

Inscreve-se em um pedaço de dado do servidor no cache através de uma chave.

useMutation

Envia requisições de criação, atualização e exclusão com estados de loading e erro.

queryKey

A identidade de uma entrada de cache, usada para ler, atualizar e invalidá-la.

staleTime

Quanto tempo o dado é considerado fresco antes de ocorrer um refetch em segundo plano.

Invalidation

Marca queries como obsoletas para que sejam recarregadas quando necessário.

Devtools

Inspecione cada query, seu estado e o cache em um painel dedicado.

Uma breve historia

A ascensão do gerenciamento de server-state

  1. 2019

    Lançamento do React Query

    Uma pequena biblioteca traz caching e gerenciamento de server-state para a busca de dados no React.

    19
  2. 2021

    React Query 3

    Adoção mais ampla e uma API madura tornam a biblioteca um padrão comum.

    21
  3. 2022

    TanStack Query

    A biblioteca é renomeada e expandida para Vue, Svelte e Solid.

    22
  4. 2023

    TanStack Query 5

    Uma API mais simples, melhor inferência de TypeScript e um bundle menor.

    23
  5. Hoje

    O padrão para server state

    Uma camada amplamente utilizada que se integra a qualquer biblioteca de client-state.

    Hoje

O guia completo

TanStack Query: Tudo que voce precisa saber

O que é TanStack Query?

TanStack Query, anteriormente conhecido como React Query, é uma biblioteca de gerenciamento de estado de servidor. Ela reconhece que dados buscados de uma API são fundamentalmente diferentes do estado que você mantém em useState. Dados de servidor são compartilhados, cacheados, podem ficar obsoletos e precisam ser buscados novamente. Gerenciar isso com effects e flags é tedioso e propenso a bugs.

O TanStack Query fornece um cache adequado para dados de servidor. Você descreve como buscar um dado e atribui a ele uma chave, e a biblioteca cuida do caching, deduplicação, estados de carregamento e erro, refetching em segundo plano e invalidação. É a camada de dados que faltava para o React, e combina com qualquer biblioteca de estado de cliente.

Queries

Uma query é definida por uma key (chave) e uma query function. A key identifica a entrada no cache; a função busca os dados.

// api.ts
async function fetchPost(id: string) {
  const res = await fetch(`/api/posts/${id}`);
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return res.json();
}
// Post.tsx
import { useQuery } from "@tanstack/react-query";

export function Post({ id }: { id: string }) {
  const { data, isLoading, isError, error } = useQuery({
    queryKey: ["post", id],
    queryFn: () => fetchPost(id),
  });

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

  return <article>{data.title}</article>;
}

Todo componente que utiliza a mesma key compartilha a mesma entrada de cache, portanto, montar o componente duas vezes resulta em apenas uma requisição. As flags de status são fornecidas automaticamente, para que você nunca precise escrevê-las manualmente.

Query keys e o cache

A chave é a identidade da entrada do cache e deve incluir todas as variáveis das quais a busca depende.

// keys.ts
useQuery({ queryKey: ["posts"], queryFn: fetchPosts });
useQuery({ queryKey: ["post", id], queryFn: () => fetchPost(id) });
useQuery({
  queryKey: ["posts", { page, status }],
  queryFn: () => fetchPosts({ page, status }),
});

Como as chaves são arrays, você pode invalidar toda uma família usando um prefixo. Invalidar ["posts"] também invalida ["posts", { page: 1 }], o que torna as atualizações de cache previsíveis.

Atualização e refetching

Duas opções controlam quando os dados são buscados novamente (refetched).

// freshness.ts
useQuery({
  queryKey: ["posts"],
  queryFn: fetchPosts,
  staleTime: 60_000, // fresh for one minute
  gcTime: 5 * 60_000, // keep unused data for five minutes
});

staleTime define por quanto tempo os dados são considerados “frescos” (fresh); enquanto estiverem frescos, nenhum refetch em segundo plano acontece. gcTime define por quanto tempo uma entrada não utilizada permanece na memória antes de ser removida pelo garbage collector. Por padrão, as queries fazem o refetch ao montar (mount), ao focar na janela (window focus) e ao reconectar, o que mantém a UI atualizada sem a necessidade de trabalho manual.

Mutations

useMutation lida com as escritas: criação, atualização e exclusão.

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

export function AddPost() {
  const queryClient = useQueryClient();

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

  return (
    <button
      onClick={() => mutation.mutate("New post")}
      disabled={mutation.isPending}
    >
      {mutation.isPending ? "Saving…" : "Add post"}
    </button>
  );
}

Após uma mutation bem-sucedida, invalidar as queries afetadas informa a todos os consumidores que devem refazer a busca (refetch). Isso mantém o servidor como a fonte da verdade e evita a manipulação manual do cache.

Updates otimistas

Para ações que devem parecer instantâneas, atualize o cache antes que o servidor responda e reverta a alteração em caso de falha.

// optimistic.ts
useMutation({
  mutationFn: toggleLike,
  onMutate: async (postId) => {
    await queryClient.cancelQueries({ queryKey: ["post", postId] });
    const previous = queryClient.getQueryData(["post", postId]);

    queryClient.setQueryData(["post", postId], (old) => ({
      ...old,
      likes: old.likes + 1,
    }));

    return { previous };
  },
  onError: (_err, postId, context) => {
    queryClient.setQueryData(["post", postId], context.previous);
  },
  onSettled: (_data, _err, postId) => {
    queryClient.invalidateQueries({ queryKey: ["post", postId] });
  },
});

onMutate aplica a alteração otimista e tira um snapshot do valor anterior, onError reverte a alteração e onSettled refaz a busca para sincronizar com o servidor.

Paginação e queries infinitas

O TanStack Query possui suporte nativo para dados paginados e infinitos. useQuery com a opção keepPreviousData torna as transições de página fluidas, e useInfiniteQuery gerencia listas baseadas em cursor com a função fetchNextPage. Como cada página faz parte do cache, a navegação para frente e para trás é instantânea.

Estado do servidor versus estado do cliente

Esta é a distinção que faz o TanStack Query fazer sentido:

  • Estado do servidor é controlado pelo servidor, compartilhado, assíncrono e pode ficar desatualizado (stale). Use TanStack Query.
  • Estado do cliente é controlado pelo navegador, síncrono e local. Use useState, Context, Zustand ou Redux.

Misturar os dois em um único store é a fonte de muita complexidade. Separá-los — TanStack Query para dados e um store pequeno para o estado da UI — mantém cada um deles simples.

Melhores práticas

  • Inclua todas as variáveis na query key.
  • Defina staleTime deliberadamente; o valor padrão de zero dispara refetches agressivamente.
  • Use mutations combinadas com invalidação em vez de atualizar manualmente o cache em todos os lugares.
  • Mantenha as funções de query pequenas e colocalizadas com a query.
  • Adicione optimistic updates apenas onde o feedback instantâneo for essencial.
  • Use as devtools para inspecionar o estado do cache durante o desenvolvimento.
  • Mantenha o estado do servidor no TanStack Query e o estado do cliente em outro lugar.

Erros comuns

  • Usar a mesma chave para inputs diferentes, resultando em dados obsoletos ou incorretos.
  • Deixar o staleTime em zero, disparando refetches constantes.
  • Duplicar dados do servidor em um store global.
  • Esquecer de invalidar após uma mutation, exibindo dados desatualizados.
  • Escrever optimistic updates sem um caminho de rollback.
  • Tratar funções de query como componentes e chamar hooks dentro delas.

Próximos passos

O TanStack Query é a maneira padrão de lidar com dados do servidor em aplicações modernas. Combine-o com a Fetch API para a camada de requisições, um store de cliente como Zustand para o estado da UI, e Redux Toolkit se você precisar de um estado global rigoroso. Para Vue, o Pinia gerencia o estado do cliente enquanto o TanStack Query cuida do lado do servidor.

Busca de dados

O useQuery faz cache, deduplica e expõe o estado. A versão com useEffect refaz a busca em cada montagem e exige flags manuais.

Preferir
const { data, isLoading, error } = useQuery({
  queryKey: ["posts"],
  queryFn: fetchPosts,
});
Evitar
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
  fetch("/api/posts")
    .then((r) => r.json())
    .then(setData)
    .finally(() => setLoading(false));
}, []);

Manter dados atualizados

Invalide a query pela chave após uma mutation para que todos os consumidores refaçam a busca. Sem manipulações manuais de cache.

Preferir
const queryClient = useQueryClient();

const mutation = useMutation({
  mutationFn: addPost,
  onSuccess: () => {
    queryClient.invalidateQueries({
      queryKey: ["posts"],
    });
  },
});
Evitar
// refetching by hand in
// every component that
// happens to show posts

Perguntas frequentes

Perguntas frequentes

Keep learning

Related topics from the roadmap.

$ comecar a aprender

Pronto para aprender TanStack Query?

Nosso tutorial interativo te guia por TanStack Query passo a passo — com quizzes e codigo real que voce pode executar no navegador.