JavaScript Avançado

Dominando React Query Avançado: Cache, Mutations, Optimistic Updates e Prefetch em Projetos Reais

7 min de leitura

Dominando React Query Avançado: Cache, Mutations, Optimistic Updates e Prefetch em Projetos Reais

Dominando o Cache do React Query O cache é o coração do React Query. A biblioteca gerencia automaticamente quando buscar dados, quando reutilizá-los e quando invalidá-los. Entender sua estratégia é fundamental para construir aplicações eficientes. Por padrão, o React Query mantém dados em cache por 5 minutos (staleTime padrão é 0, mas o cacheTime é 5 minutos). Dados "stale" ainda são retornados instantaneamente, mas uma nova busca é disparada em background. Isso evita requisições desnecessárias enquanto mantém dados frescos. Para estratégias mais avançadas, use queryKey como array com identificadores. Isso permite invalidar caches granulares e fazer prefetch eficiente de dados relacionados. Mutations: Alterando Dados com Controle Total Mutations são operações que modificam estado no servidor. Diferente de queries, elas não são automáticas e você tem controle explícito sobre quando executá-las. O hook retorna uma função mutate que você invoca quando necessário. Ele fornece callbacks úteis: onSuccess (após sucesso), onError (se falhar) e onMutate (antes de executar, essencial para otimistic updates).

<h2>Dominando o Cache do React Query</h2>

<p>O cache é o coração do React Query. A biblioteca gerencia automaticamente quando buscar dados, quando reutilizá-los e quando invalidá-los. Entender sua estratégia é fundamental para construir aplicações eficientes.</p>

<p>Por padrão, o React Query mantém dados em cache por 5 minutos (staleTime padrão é 0, mas o cacheTime é 5 minutos). Dados &quot;stale&quot; ainda são retornados instantaneamente, mas uma nova busca é disparada em background. Isso evita requisições desnecessárias enquanto mantém dados frescos.</p>

<pre><code class="language-javascript">import { useQuery } from &#039;@tanstack/react-query&#039;;

const fetchUsers = async () =&gt; {

const res = await fetch(&#039;/api/users&#039;);

return res.json();

};

export function UserList() {

const { data, isLoading } = useQuery({

queryKey: [&#039;users&#039;],

queryFn: fetchUsers,

staleTime: 1000 60 5, // 5 minutos

gcTime: 1000 60 10, // Remove do cache após 10 min de inatividade

});

if (isLoading) return &lt;div&gt;Carregando...&lt;/div&gt;;

return &lt;ul&gt;{data?.map(u =&gt; &lt;li key={u.id}&gt;{u.name}&lt;/li&gt;)}&lt;/ul&gt;;

}</code></pre>

<p>Para estratégias mais avançadas, use queryKey como array com identificadores. Isso permite invalidar caches granulares e fazer prefetch eficiente de dados relacionados.</p>

<h2>Mutations: Alterando Dados com Controle Total</h2>

<p>Mutations são operações que modificam estado no servidor. Diferente de queries, elas não são automáticas e você tem controle explícito sobre quando executá-las.</p>

<p>O <code>useMutation</code> hook retorna uma função mutate que você invoca quando necessário. Ele fornece callbacks úteis: onSuccess (após sucesso), onError (se falhar) e onMutate (antes de executar, essencial para otimistic updates).</p>

<pre><code class="language-javascript">import { useMutation, useQueryClient } from &#039;@tanstack/react-query&#039;;

const createUser = async (newUser) =&gt; {

const res = await fetch(&#039;/api/users&#039;, {

method: &#039;POST&#039;,

headers: { &#039;Content-Type&#039;: &#039;application/json&#039; },

body: JSON.stringify(newUser),

});

return res.json();

};

export function CreateUserForm() {

const queryClient = useQueryClient();

const mutation = useMutation({

mutationFn: createUser,

onSuccess: (data) =&gt; {

// Invalida cache e refetch automático

queryClient.invalidateQueries({ queryKey: [&#039;users&#039;] });

},

onError: (error) =&gt; {

console.error(&#039;Erro ao criar usuário:&#039;, error);

},

});

const handleSubmit = (e) =&gt; {

e.preventDefault();

const formData = new FormData(e.target);

mutation.mutate({ name: formData.get(&#039;name&#039;), email: formData.get(&#039;email&#039;) });

};

return (

&lt;form onSubmit={handleSubmit}&gt;

&lt;input name=&quot;name&quot; placeholder=&quot;Nome&quot; required /&gt;

&lt;input name=&quot;email&quot; placeholder=&quot;Email&quot; required /&gt;

&lt;button disabled={mutation.isPending}&gt;

{mutation.isPending ? &#039;Salvando...&#039; : &#039;Criar&#039;}

&lt;/button&gt;

{mutation.isError &amp;&amp; &lt;p&gt;{mutation.error.message}&lt;/p&gt;}

&lt;/form&gt;

);

}</code></pre>

<h2>Optimistic Updates: UX Instantânea</h2>

<p>Optimistic updates atualizam a UI imediatamente, assumindo que a operação terá sucesso. Se falhar, você reverte. Isso cria uma experiência fluida sem esperar pela resposta do servidor.</p>

<p>A chave está no <code>onMutate</code>: você acessa o cache atual, atualiza-o otimistamente e retorna um contexto para reverter se necessário. Use <code>setQueryData</code> para manipular o cache manualmente.</p>

<pre><code class="language-javascript">import { useMutation, useQueryClient } from &#039;@tanstack/react-query&#039;;

const updateUserStatus = async (userId, status) =&gt; {

const res = await fetch(/api/users/${userId}, {

method: &#039;PATCH&#039;,

headers: { &#039;Content-Type&#039;: &#039;application/json&#039; },

body: JSON.stringify({ status }),

});

return res.json();

};

export function UserStatusToggle({ userId, currentStatus }) {

const queryClient = useQueryClient();

const mutation = useMutation({

mutationFn: ({ id, status }) =&gt; updateUserStatus(id, status),

onMutate: async ({ id, status }) =&gt; {

// Cancela requisições em background

await queryClient.cancelQueries({ queryKey: [&#039;users&#039;] });

// Salva estado anterior

const previousUsers = queryClient.getQueryData([&#039;users&#039;]);

// Atualiza cache otimistamente

queryClient.setQueryData([&#039;users&#039;], (old) =&gt;

old?.map(u =&gt; u.id === id ? { ...u, status } : u)

);

return { previousUsers }; // Contexto para rollback

},

onError: (err, variables, context) =&gt; {

// Reverte em caso de erro

if (context?.previousUsers) {

queryClient.setQueryData([&#039;users&#039;], context.previousUsers);

}

},

onSuccess: () =&gt; {

// Sincroniza com servidor após sucesso (opcional)

queryClient.invalidateQueries({ queryKey: [&#039;users&#039;] });

},

});

const newStatus = currentStatus === &#039;active&#039; ? &#039;inactive&#039; : &#039;active&#039;;

return (

&lt;button

onClick={() =&gt; mutation.mutate({ id: userId, status: newStatus })}

disabled={mutation.isPending}

&gt;

{mutation.isPending ? &#039;Atualizando...&#039; : Marcar como ${newStatus}}

&lt;/button&gt;

);

}</code></pre>

<h2>Prefetch e Estratégias de Carregamento</h2>

<p>Prefetch carrega dados antes do usuário realmente precisar deles. É perfeito para navegar entre páginas ou expandir listas. Use <code>prefetchQuery</code> para disparar requisições em background sem bloquear a UI.</p>

<p>Uma estratégia comum é prefetch ao passar o mouse sobre um link, ou prefetch dados relacionados assim que a página carrega. Isso reduz waterfalls de requisições e melhora drasticamente a percepção de velocidade.</p>

<pre><code class="language-javascript">import { useQuery, usePrefetchQuery } from &#039;@tanstack/react-query&#039;;

const fetchUserDetails = async (userId) =&gt; {

const res = await fetch(/api/users/${userId});

return res.json();

};

export function UserPreview({ userId }) {

const prefetch = usePrefetchQuery();

const handleMouseEnter = () =&gt; {

// Dispara prefetch ao passar o mouse

prefetch({

queryKey: [&#039;userDetails&#039;, userId],

queryFn: () =&gt; fetchUserDetails(userId),

staleTime: 1000 * 60, // 1 minuto

});

};

return (

&lt;div onMouseEnter={handleMouseEnter}&gt;

&lt;a href={/users/${userId}}&gt;Ver Perfil&lt;/a&gt;

&lt;/div&gt;

);

}

export function UserDetails({ userId }) {

// Dados já podem estar em cache pelo prefetch

const { data, isLoading } = useQuery({

queryKey: [&#039;userDetails&#039;, userId],

queryFn: () =&gt; fetchUserDetails(userId),

});

if (isLoading) return &lt;div&gt;Carregando...&lt;/div&gt;;

return &lt;div&gt;{data?.name} - {data?.email}&lt;/div&gt;;

}</code></pre>

<p>Outra abordagem é prefetch em paralelo quando uma página carrega. Isso é útil em dashboards onde você quer ter múltiplos dados prontos simultaneamente.</p>

<h2>Conclusão</h2>

<p>Você aprendeu que <strong>cache eficiente</strong> com staleTime e gcTime evita requisições desnecessárias. <strong>Mutations com callbacks</strong> oferecem controle fino sobre operações de escrita. <strong>Optimistic updates</strong> criam experiências instantâneas ao atualizar o cache antes da resposta do servidor chegar, revertendo se houver erro. <strong>Prefetch</strong> reduz latência ao carregar dados proativamente.</p>

<p>Domine esses conceitos e você construirá interfaces altamente responsivas, com gerenciamento de estado servidor simplificado e experiência de usuário excepcional.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://tanstack.com/query/latest" target="_blank" rel="noopener noreferrer">React Query Official Docs</a></li>

<li><a href="https://tanstack.com/query/latest/docs/framework/react/guides/important-defaults" target="_blank" rel="noopener noreferrer">Caching Strategies Guide</a></li>

<li><a href="https://tkdodo.eu/blog/react-query" target="_blank" rel="noopener noreferrer">TkDodo&#039;s React Query Course</a></li>

<li><a href="https://tanstack.com/query/latest/docs/framework/react/guides/optimistic-updates" target="_blank" rel="noopener noreferrer">Optimistic Updates Pattern</a></li>

<li><a href="https://github.com/tanstack/query" target="_blank" rel="noopener noreferrer">React Query GitHub Repository</a></li>

</ul>

Comentários

Mais em JavaScript Avançado

Guia Completo de Construindo CLI Profissional em Node.js com commander e inquirer
Guia Completo de Construindo CLI Profissional em Node.js com commander e inquirer

Introdução ao Commander e Inquirer Construir interfaces de linha de comando (...

Guia Completo de Generics Avançados em TypeScript: Constraints, Defaults e Variância
Guia Completo de Generics Avançados em TypeScript: Constraints, Defaults e Variância

Constraints: Limitando Tipos Generics Os constraints definem quais tipos pode...

O que Todo Dev Deve Saber sobre Web Workers: Paralelismo Real no Navegador com JavaScript
O que Todo Dev Deve Saber sobre Web Workers: Paralelismo Real no Navegador com JavaScript

Web Workers: Paralelismo Real no Navegador com JavaScript Web Workers represe...