Skip to content
Anunciando SWR 2.0

Anunciando SWR 2.0

Nós estamos muito felizes em anunciar o lançamento do SWR 2.0, a popular biblioteca de busca de dados para React que permite que componentes busquem, armazenem em cache, alterem dados e que mantém a UI atualizada com as alterações nesses dados ao longo do tempo.

Essa nova versão chega carregada com melhorias e novas funcionalidades, como novas APIs de mutação, melhorias nas capacidades de UI otimista, novas ferramentas de desenvolvedor e melhor suporte para renderização concorrente. Gostaríamos de agradecer a todos os contribuidores e mantenedores que tornaram esse lançamento possível.

Mutação e UI Otimista

useSWRMutation

Mutação é uma parte importante do processo de data fetching. Elas permitem que você faça alterações em seus dados tanto localmente quanto remotamente. Nossa API existente mutate permite que você revalide e altere recursos manualmente. No SWR 2.0, o novo hook useSWRMutation torna ainda mais simples alterar dados remotamente usando uma API declarativa. Você pode configurar uma mutação usando o hook e em seguida, ativá-la mais tarde:

import useSWRMutation from 'swr/mutation'
 
async function sendRequest(url, { arg }) {
  return fetch(url, {
    method: 'POST',
    body: JSON.stringify(arg)
  })
}
 
function App() {
  const { trigger, isMutating } = useSWRMutation('/api/user', sendRequest)
 
  return (
    <button
      disabled={isMutating}
      onClick={() => trigger({ username: 'johndoe' })}
    >{
      isMutating ? 'Criando...' : 'Criar usuário'
    }</button>
  )
}

O exemplo acima define uma mutação sendRequest que afeta o recurso '/api/user'. Ao contrário de useSWR, useSWRMutation não iniciará imediatamente a requisição ao renderizar. Em vez disso, ele retorna uma função trigger que pode ser chamada posteriormente para iniciar manualmente a mutação.

A função sendRequest será chamada quando o botão for clicado, junto com o argumento extra { username: 'johndoe' }. O valor de isMutating será definido como true até que a mutação tenha terminado.

Adicionalmente, esse novo hook resolve outros problemas que você pode ter com mutações:

  • Atualiza a UI otimisticamente enquanto os dados estão sendo alterados
  • Reverte automaticamente quando a mutação falha
  • Evita qualquer potencial conflito entre useSWR e outras mutações do mesmo recurso
  • Preenche o cache do useSWR após a mutação ser concluída
  • ...

Você pode achar referências de API e exemplos mais detalhados lendo a documentação ou rolando pelas próximas seções.

UI Otimista

UI Otimista é um exelente modelo para criar sites que sejam rápidos e responsivos; no entanto, pode ser difícil implementá-lo corretamente. O SWR 2.0 adicionou algumas novas opções poderosas para facilitar isso.

Vamos supor que tenhamos uma API que adiciona um novo item à lista de tarefas e o envia ao servidor:

await addNewTodo('Novo Item')

Na nossa UI, usamos um hook useSWR para exibir a lista de tarefas, com um botão “Adicionar novo item” que dispara essa requisição e pede ao SWR para rebuscar os dados via mutate():

const { mutate, data } = useSWR('/api/todos')
 
return <>
  <ul>{/* Exibir dados */}</ul>
 
  <button onClick={async () => {
    await addNewTodo('Novo Item')
    mutate()
  }}>
    Adicionar novo item
  </button>
</>

Entretanto, a requisição await addNewTodo(...) pode ser muito lenta. Quando estiver em andamento, os usuários ainda verão a lista antiga, mesmo que já saibamos como será a nova lista. Com a nova opção optimisticData, podemos mostrar a nova lista otimisticamente, antes que o servidor responda:

const { mutate, data } = useSWR('/api/todos')
 
return <>
  <ul>{/* Exibir dados */}</ul>
 
  <button onClick={() => {
    mutate(addNewTodo('Novo Item'), {
      optimisticData: [...data, 'Novo Item'],
    })
  }}>
    Adicionar novo item
  </button>
</>

O SWR irá imediatamente atualizar data com o valor de optimisticData e, em seguida, enviar a requisição ao servidor. Assim que a requisição terminar, o SWR revalidará o recurso para garantir que seja o mais recente.

Assim como muitas APIs, se a requisição addNewTodo(...) retornar os dados mais recentes do servidor, também podemos exibir diretamente esse resultado (em vez de iniciar uma nova revalidação)! Existe a nova opção populateCache para dizer ao SWR para atualizar os dados locais com a resposta da mutação:

const { mutate, data } = useSWR('/api/todos')
 
return <>
  <ul>{/* Exibir dados */}</ul>
 
  <button onClick={() => {
    mutate(addNewTodo('Novo Item'), {
      optimisticData: [...data, 'Novo Item'],
      populateCache: true,
    })
  }}>
    Adicionar novo item
  </button>
</>

Ao mesmo tempo, não precisamos de outra revalidação depois, pois os dados da resposta são da fonte da verdade, podemos desativá-la com a opção revalidate:

const { mutate, data } = useSWR('/api/todos')
 
return <>
  <ul>{/* Exibir dados */}</ul>
 
  <button onClick={() => {
    mutate(addNewTodo('Novo Item'), {
      optimisticData: [...data, 'Novo Item'],
      populateCache: true,
      revalidate: false,
    })
  }}>
    Adicionar novo item
  </button>
</>

Por último, se addNewTodo(...) falhar com uma exceção, podemos reverter os dados otimistas ([...data, 'Novo Item']) que acabamos de definir, definindo rollbackOnError como true (que também é a opção padrão). Quando isso acontece, o SWR reverterá data para o valor anterior.

const { mutate, data } = useSWR('/api/todos')
 
return <>
  <ul>{/* Exibir dados */}</ul>
 
  <button onClick={() => {
    mutate(addNewTodo('Novo Item'), {
      optimisticData: [...data, 'Novo Item'],
      populateCache: true,
      revalidate: false,
      rollbackOnError: true,
    })
  }}>
    Adicionar novo item
  </button>
</>

Todas essas APIs são suportadas no novo hook useSWRMutation também. Para saber mais sobre elas, você pode conferir nossa documentação. Aqui está uma demo mostrando esse comportamento:

UI Otimista com reversão automática de erros

Modificando Múltiplas Chaves

A API global mutate agora aceita uma função de filtro, onde você pode mutar ou revalidar chaves específicas. Isso será útil para casos de uso como invalidar todos os dados em cache. Para saber mais, você pode ler Modificando Múltiplas Chaves na documentação.

import { mutate } from 'swr'
// Ou do hook se você tiver customizado seu provedor de cache:
// { mutate } = useSWRConfig()
 
// Modificar um único recurso
mutate(key)
 
// Modificar múltiplos recursos e limpar o cache (definir como undefined)
mutate(
  key => typeof key === 'string' && key.startsWith('/api/item?id='),
  undefined,
  { revalidate: false }
)

SWR DevTools

O SWRDevTools (opens in a new tab) é uma extensão do navegador que ajuda você a depurar o cache do SWR e os resultados das requisições. Confira nossa seção de DevTools para saber como usar o devtools em sua aplicação.

Pré-carregando Dados

Precarregar dados pode melhorar a experiência do usuário tremendamente. Se você souber que o recurso será usado mais tarde na aplicação, pode usar a nova API preload para começar a carregá-lo mais cedo:

import useSWR, { preload } from 'swr'
 
const fetcher = (url) => fetch(url).then((res) => res.json())
 
// Você pode chamar a função preload em qualquer lugar
preload('/api/user', fetcher)
 
function Profile() {
  // O componente que realmente usa os dados:
  const { data, error } = useSWR('/api/user', fetcher)
  // ...
}
 
export function Page () {
  return <Profile/>
}

Nesse exemplo, a preload API é chamada no escopo global. Isso significa que começamos a pré-carregar o recurso antes mesmo que o React comece a renderizar qualquer coisa. E quando o componente Profile estiver sendo renderizado, os dados provavelmente já estarão disponíveis. Se ainda estiver em andamento, o hook useSWR reutilizará essa solicitação de pré-carregamento em andamento em vez de iniciar uma nova.

A preload API também pode ser usada em casos como pré-carregar dados para outra página que provavelmente será renderizada. Mais informações sobre pré-carregamento de dados com SWR podem ser encontradas aqui.

isLoading

isLoading é o novo estado retornado pelo useSWR, que indica se a requisição ainda está em andamento e ainda não há dados carregados. Anteriormente, o estado isValidating representava tanto o estado de carregamento inicial quanto o estado de revalidação, então tínhamos que verificar se tanto data quanto error eram undefined para determinar se era o estado de carregamento inicial.

Agora, é tão fácil que você pode usar o valor isLoading diretamente para renderizar uma mensagem de carregamento:

import useSWR from 'swr'
 
function Profile() {
  const { data, isLoading } = useSWR('/api/user', fetcher)
 
  if (isLoading) return <div>carregando...</div>
  return <div>olá {data.name}!</div>
}

Note que isValidating ainda está presente, então você ainda pode usá-lo para mostrar um indicador de carregamento para revalidações.

📝

Nós adicionamos a nova página Entendendo SWR para descrever como o SWR retorna valores, que inclui a diferença entre isValidating e isLoading, e como combiná-los para melhorar a experiência do usuário.

Preservando o Estado Anterior

A opção keepPreviousData é uma nova adição que permite manter os dados que foram buscados anteriormente. Isso melhora a UX imensamente quando você está buscando dados com base em ações do usuário acontecendo em tempo real, como com um recurso de pesquisa ao vivo, onde a key do recurso continua mudando:

function Search() {
  const [search, setSearch] = React.useState('');
 
  const { data, isLoading } = useSWR(`/search?q=${search}`, fetcher, {
    keepPreviousData: true
  })
 
  return (
    <div>
      <input
        type="text"
        value={search}
        onChange={(e) => setSearch(e.target.value)}
        placeholder="Pesquisar..."
      />
 
      <div className={isLoading ? "loading" : ""}>
        {data?.products.map(item => <Product key={item.id} name={item.name} />)
      </div>
    </div>
  );
}
Preserve os resultados de pesquisa anteriores quando keepPreviousData estiver habilitado

Dê uma olhada no código no CodeSandbox (opens in a new tab) e você pode ler mais sobre isso aqui.

Extendendo Configurações

O SWRConfig agora pode aceitar uma função como valor. Quando você tem vários níveis de <SWRConfig>, o interno recebe a configuração do pai e retorna uma nova. Essa mudança torna mais flexível configurar o SWR em uma codebase grande. Mais informações podem ser encontradas aqui.

<SWRConfig
  value={parentConfig => ({
    dedupingInterval: parentConfig.dedupingInterval * 5,
    refreshInterval: 100,
  })}
>
  <Page />
</SWRConfig>

Suporte Melhorado ao React 18

SWR has updated its internal code to use useSyncExternalStore and startTransition APIs in React 18. These ensure stronger consistency when rendering UI concurrently. This change doesn’t require any user code changes and all developers will benefit from it directly. Shims are included for React 17 and below.

O SWR atualizou seu código interno para usar o useSyncExternalStore e as startTransition APIs no React 18. Isso garante uma consistência mais forte ao renderizar a UI concorrentemente. Essa mudança não requer nenhuma alteração no código do usuário e todos os desenvolvedores se beneficiarão diretamente. Shims são incluídos para o React 17 e abaixo.

SWR 2.0 e todas as novas funcionalidades ainda são compatíveis com o React 16 e 17.

Guia de Migração

Fetcher não recebe mais múltiplos argumentos

key agora é passado como um argumento único.

- useSWR([1, 2, 3], (a, b, c) => {
+ useSWR([1, 2, 3], ([a, b, c]) => {
  assert(a === 1)
  assert(b === 2)
  assert(c === 3)
})

Mutação Global não recebe mais uma função getKey

Agora, se você passar uma função para o mutate global, ela será usada como um filtro. Anteriormente, você podia passar uma função que retornasse uma chave para o mutate global:

- mutate(() => '/api/item') // uma função para retornar a chave
+ mutate('/api/item')       // para modificar a chave, passe-a diretamente

Nova propriedade obrigatória keys() para a interface Cache

Quando você usar sua própria implementação de cache, a interface Cache agora requer um método keys() que retorna todas as chaves no objeto cache, similar às instâncias Map do JavaScript.

interface Cache<Data> {
  get(key: string): Data | undefined
  set(key: string, value: Data): void
  delete(key: string): void
+ keys(): IterableIterator<string>
}

Estrutura interna do Cache alterada

A estrutura interna dos dados do cache será um objeto que contém todos os estados atuais.

- assert(cache.get(key) === data)
+ assert(cache.get(key) === { data, error, isValidating })
 
// getter
- cache.get(key)
+ cache.get(key)?.data
 
// setter
- cache.set(key, data)
+ cache.set(key, { ...cache.get(key), data })
🚨

Você não deveria escrever no cache diretamente, isso pode causar comportamento indefinido.

SWRConfig.default Foi Renomeado para SWRConfig.defaultValue

O SWRConfig.defaultValue é a propriedade para acessar a configuração padrão do SWR.

- SWRConfig.default
+ SWRConfig.defaultValue

O tipo InfiniteFetcher foi renomeado para SWRInfiniteFetcher

- import type { InfiniteFetcher } from 'swr/infinite'
+ import type { SWRInfiniteFetcher } from 'swr/infinite'

Evitando Suspense no Servidor

Se você quer usar suspense: true com o SWR no lado do servidor, incluindo pré-renderização no Next.js, então você deve fornecer dados iniciais via fallbackData ou fallback.

Hoje, isso significa que você não pode usar Suspense para buscar dados no lado do servidor. Suas outras duas opções são fazer a busca de dados totalmente no lado do cliente ou obter seu framework para buscar os dados para você (como getStaticProps faz no Next.js).

ES2018 como alvo de compilação

Se você quiser dar suporte ao IE 11, você deve definir o ES5 como alvo de compilação em seu framework ou bundler. Essa mudança fez uma melhoria de desempenho no SSR e mantém o tamanho do pacote pequeno.

Changelog

Leia o Changelog completo no GitHub (opens in a new tab).

O futuro e Obrigado!

Com o novo lançamento do Next.js 13 (opens in a new tab), vemos muitas coisas novas e emocionantes, além de mudanças de paradigma no ecossistema React: React Server Components (opens in a new tab), streaming SSR, async components (opens in a new tab), e o hook use (opens in a new tab). Muitos deles estão relacionados ao data-fetching, e alguns deles têm casos de uso semelhantes com o SWR.

No entanto, o objetivo do projeto SWR permanece o mesmo. Queremos que seja uma biblioteca drop-in que seja leve, agnóstica de framework e um pouco opinativa (por exemplo, revalidar ao foco). Em vez de tentar ser uma solução padrão, queremos nos concentrar em inovações que melhorem a UX. Enquanto isso, também estamos fazendo pesquisas sobre como melhorar o SWR com essas novas capacidades do React.

Queremos agradecer a cada um dos 143 (opens in a new tab) contribuidores (+ 106 (opens in a new tab) contribuidores de documentação), bem como aqueles que nos ajudam ou nos dão feedback. Um agradecimento especial vai para Toru Kobayashi (opens in a new tab) por todo o seu trabalho nos DevTools e na documentação - não teríamos conseguido sem você!