Desenvolvimento Web

React Query: Arquitetura Completa – Do Hook ao Render

React Query: Arquitetura Completa - Do Hook ao Render

Se você já passou algum tempo construindo aplicações React que se comunicam com um backend, você provavelmente sentiu o peso de gerenciar o estado do servidor. A dança de isLoading, isError, lidando com atualizações de dados, respostas otimistas e – o grande – lidando com cache, pode rapidamente se transformar em um grande fardo de desenvolvimento. Eu já estive lá, escrevendo hooks personalizados que se tornaram emaranhados, tentando sincronizar dados manualmente entre componentes. Funcionou, mais ou menos, mas exigiu atenção constante e, honestamente, parecia que eu estava reinventando a roda o tempo todo.

Aqui está a coisa: a maioria dos problemas que enfrentamos com o estado do servidor são problemas resolvidos. E é exatamente onde o React Query (ou TanStack Query, como é conhecido agora) brilha. Não é apenas uma biblioteca; é uma poderosa ferramenta de gerenciamento de estado projetada especificamente para dados assíncronos, mudando fundamentalmente a forma como pensamos sobre buscar, cache e atualizar dados do servidor. Em minha experiência, entender sua arquitetura não é apenas acadêmico; é realmente libertador.

Hoje, vamos tirar o véu e traçar o fluxo completo: do momento em que você chama useQuery em seu componente, passando pelos mecanismos intricados do QueryClient, até como ele inteligentemente dispara uma re-renderização.

O problema: por que o estado do servidor precisa de tratamento especial?

Antes de mergulharmos na solução, vamos recapitular os pontos de dor que o React Query aborda:

* Loading & Error States: o boilerplate de if (isLoading) return … e if (isError) return … se torna repetitivo.
* Cache: como evitar refazer a mesma solicitação de dados repetidamente? E como saber quando os dados em cache estão “stale”?
* Sincronização: se os dados mudam no servidor, como todos os componentes que exibem esses dados são atualizados sem uma atualização completa da página?
* Condições de corrida: o que acontece se um componente é desmontado enquanto uma solicitação está em andamento?
* Desempenho: como fazer com que o aplicativo se sinta ágil, mesmo com condições de rede lentas?

Os gerenciadores de estado do lado do cliente, como Redux ou Zustand, são fantásticos para o estado do cliente – temas da interface do usuário, visibilidade do modal, entradas de formulário. No entanto, eles muitas vezes se tornam difíceis de usar ao tentar gerenciar o estado do servidor – dados que vivem remotamente, mudam inesperadamente e precisam ser buscados assincronamente. O estado do servidor tem características diferentes; não é algo que o seu aplicativo “possui”. O React Query é construído do zero para respeitar essas diferenças.

Os principais jogadores: QueryClient e useQuery

No coração da arquitetura do React Query, existem dois elementos-chave:

* QueryClient: é o cérebro do seu setup do React Query. Ele mantém todo o cache, gerencia instâncias de consulta, lida com buscas e notifica componentes inscritos quando os dados mudam. Você normalmente cria uma instância de QueryClient e a passa por meio de um QueryClientProvider na raiz do seu aplicativo.
* useQuery (e seus irmãos useMutation, useInfiniteQuery, etc.): é o hook React que você usa em seus componentes para interagir com o QueryClient. É a porta de entrada do seu componente para buscar, cache e se inscrever no estado do servidor.

Vamos ilustrar com um exemplo simples: buscar uma lista de posts.

// src/App.tsx
import { QueryClient, QueryClientProvider } from ‘@tanstack/react-query’;
import { PostsList } from ‘./PostsList’;

const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60 * 5, // Dados considerados frescos por 5 minutos
cacheTime: 1000 * 60 * 10, // Consultas inativas são coletadas após 10 minutos
},
},
});

function App() {
return (

Meu Blog Incrível


);
}

export default App;

// src/PostsList.tsx
import { useQuery } from ‘@tanstack/react-query’;
import axios from ‘axios’;

interface Post {
id: number;
title: string;
body: string;
}

const fetchPosts = async (): Promise => {
const { data } = await axios.get(‘https://jsonplaceholder.typicode.com/posts’);
return data;
};

export function PostsList() {
const { data, isLoading, isError, error } = useQuery({
queryKey: [‘posts’], // Chave única para essa consulta
queryFn: fetchPosts, // Função para buscar os dados
});

if (isLoading) return

Carregando posts…

;
if (isError) return

Erro: {error?.message}

;

return (

{data?.map((post) => (

{post.title}

{post.body.substring(0, 100)}…

))}

);
}

O fluxo completo: do hook ao render

Agora, vamos traçar exatamente o que acontece quando o PostsList é renderizado:

1. O componente é montado e o useQuery é executado.
2. O useQuery se registra como um observador.
3. O QueryClient verifica seu cache (e a queryKey é a rainha!).
4. Se os dados existirem e estiverem frescos, o QueryClient os retorna imediatamente.
5. Se os dados existirem, mas estiverem estalados, o QueryClient os retorna imediatamente e, em seguida, inicia uma refetch em segundo plano.
6. Se não houver dados no cache, o QueryClient define isLoading como true e executa a queryFn para buscar os dados da rede.
7. O QueryClient atualiza seu cache.
8. O QueryClient notifica os observadores.
9. O useQuery recebe a atualização e dispara uma re-renderização.

Lições aprendidas

* As chaves de consulta são seu melhor amigo (e pior inimigo): quase todos os problemas de cache ou refetch inesperados vêm de um uso mal compreendido da queryKey. Elas não são apenas rótulos; são as dependências da sua consulta. Mudar uma chave significa uma nova consulta.
* Abrace o staleTime: o staleTime padrão de 0 significa que os dados são considerados estalados imediatamente após o fetch. Isso é seguro, mas nem sempre ótimo. Eu geralmente defino o staleTime para alguns segundos ou até minutos para dados que não mudam com frequência. Isso faz com que a interface do usuário se sinta incrivelmente rápida, pois renderizações subsequentes muitas vezes atingem o cache fresco.
* O poder do cacheTime: o cacheTime (padrão de 5 minutos) determina por quanto tempo as consultas inativas permanecem no cache antes de serem coletadas. Não confunda com o staleTime. O cacheTime é sobre gerenciamento de memória para componentes desmontados.
* Estabilidade do QueryClient: certifique-se de que a instância do QueryClient seja estável (criada fora da árvore de componentes ou memorizada). Criar um novo cliente a cada renderização quebrará o cache e as assinaturas.
* Selecione para desempenho: em vez de buscar objetos enormes e usar apenas uma pequena parte, o select permite transformar ou selecionar dados específicos após serem buscados, mas antes de serem retornados ao useQuery. Isso pode evitar re-renderizações desnecessárias se os dados transformados não mudaram, mesmo que os dados brutos tenham mudado.

Pensamentos finais

O React Query é um poderoso aliado no desenvolvimento moderno do React. Ao entender seu fluxo arquitetônico – a interação entre as chamadas do useQuery, o cache central do QueryClient e seu padrão de observador inteligente – você desbloqueia um mundo de otimizações automatizadas: refetch em segundo plano, cache inteligente e sincronização de dados sem esforço.

Isso o liberta do tedioso boilerplate do gerenciamento de dados, permitindo que você se concentre em construir recursos que realmente importam. Eu já vi isso transformar projetos de pesadelos de dados em aplicativos ágeis e de alto desempenho. É mais do que apenas uma biblioteca de busca de dados; é um sistema opinativo e robusto para gerenciar o estado do servidor que, uma vez adotado, você se perguntará como vivia sem ele.

Vamos manter a conversa!

Se você achou isso interessante, eu adoraria que você verificasse mais do meu trabalho ou simplesmente dissesse olá.

✍️ Leia mais no meu blog: bishoy-bishai.github.io
☕ Vamos conversar no LinkedIn: linkedin.com/in/bishoybishai

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *