Capítulo 32 de 456

SWR

Core Idea

Use SWR pra buscar dado em Client Components, fornecer dado inicial a partir de Server Components via <SWRConfig fallback>, e coordenar mutations do browser com dado server cacheado através de uma cache key/tag compartilhada.

Key Concepts

  • useSWR(key, fetcher, options): hook principal; key condicional (query ? url : null) atrasa a requisição até ter input.
  • suspense: true: delega o loading state ao Suspense boundary mais próximo; revalidações subsequentes mantêm o dado atual em tela (não voltam ao fallback).
  • isLoading vs isValidating: isLoading é true quando há requisição rodando e nenhum dado carregado; isValidating é true sempre que há requisição rodando, incluindo revalidação em background.
  • <SWRConfig fallback>: escopar ao segmento de rota dono do dado; a key do fallback e do useSWR devem ser idênticas ou SWR ignora o fallback e busca no client.
  • revalidateIfStale: false: pula revalidação ao montar com dado cacheado (aplica-se a cada mount, diferente do staleTime do TanStack Query).
  • refreshInterval: revalidação por polling agendado.
  • mutate(key, updater, options): dispara write com optimisticData, revalidate: false, rollbackOnError: true, throwOnError: false.
  • Cache contract compartilhado: objeto com key(id) e tag(id) usado tanto pelo fallback/useSWR (key) quanto pelo cacheTag server (tag); deve ficar livre de imports server-only/client-only.

Code Examples

'use client'
import useSWR from 'swr'

export function ProductAutocomplete({ query }: { query: string }) {
  const { data = [], error, isLoading } = useSWR(
    query ? `/api/products?query=${encodeURIComponent(query)}` : null,
    fetcher
  )
  if (!query) return null
  if (error) return <p>Failed to load products.</p>
  if (isLoading) return <p>Loading products...</p>
  return <ul>{data.map((p) => <li key={p.id}>{p.name}</li>)}</ul>
}
  • O que demonstra: key condicional que atrasa fetch até haver input, com loading/error inline.
export const productCache = {
  key: (id: string) => `/api/products/${id}`,
  tag: (id: string) => `product:${id}`,
}
  • O que demonstra: contrato único de key (client) + tag (server) reutilizado por fallback, useSWR e cacheTag.
'use client'
import { useSWRConfig } from 'swr'

export function MarkReadButton() {
  const { mutate } = useSWRConfig()
  function markRead() {
    return mutate(
      activityCache.key,
      async () => { await markActivityReadAction(); return { count: 0 } },
      { optimisticData: { count: 0 }, revalidate: false, rollbackOnError: true, throwOnError: false }
    )
  }
  return <button onClick={markRead}>Mark read</button>
}
  • O que demonstra: update otimista com rollback automático em caso de erro, coordenado com Server Action que chama updateTag.

Anti-patterns

  • Key do fallback diferente da key do useSWR: SWR ignora o fallback silenciosamente e refaz o fetch no client. Defina a key uma única vez e importe dos dois lugares.
  • Esperar freshness baseada em tempo do fallback: SWR não tem janela de staleness por tempo, apenas revalidateIfStale. Não confunda com staleTime do TanStack Query.

Key Takeaways

  1. SWR 2.3.0+ com React 19 permite Server Component fornecer fallback data sem await (o componente que lê a key suspende).
  2. Fallback é tratado como stale por padrão; SWR revalida no browser após hidratação a menos que revalidateIfStale: false.
  3. A key SWR deve apontar pra um Route Handler GET que reusa a mesma função server (getProduct) usada no fallback.
  4. Combine com Cache Components (use cache + cacheLife + cacheTag) pra cachear o dado server que alimenta o fallback; a cache do SWR é independente e não precisa das mesmas durações.
  5. Após mutation, chame updateTag/revalidateTag no Server Action pra invalidar o read server cacheado com a mesma tag do contrato.

Connects To

  • ch031 Client-side data fetching: framework de decisão de padrão (inline/Suspense/server-provided).
  • ch033 TanStack Query: alternativa equivalente com API diferente (queryKey em vez de string key).
  • use cache / cacheTag / updateTag: mecanismo server-side coordenado via cache contract.