Uma introdução ao SWR: ganchos de reação para busca remota de dados
Publicados: 2022-03-10SWR é uma biblioteca leve criada pela Vercel (anteriormente ZEIT) que permite buscar, armazenar em cache ou buscar novamente dados em tempo real usando React Hooks. Ele é construído com o React Suspense, que permite que seus componentes “esperem” por algo antes de serem renderizados, incluindo dados. O SWR também vem com ótimos recursos, como busca dependente, foco na revalidação, recuperação de posição de rolagem e assim por diante. Também é uma ferramenta muito poderosa, pois é agnóstica de back-end e tem um bom suporte para TypeScript. É um pacote que tem um futuro brilhante.
Por que você deveria se importar? Você deve se importar se estiver procurando por uma biblioteca que não apenas busque dados de APIs, mas também possibilite fazer coisas como armazenamento em cache e busca dependente. O que será abordado neste tutorial será útil ao construir aplicativos React com muitas partes móveis. Espera-se que você tenha feito uso do Axios e da API Fetch, embora comparemos como eles diferem do SWR, não entraremos em detalhes sobre como eles serão implementados.
Neste guia, apresentarei React Hooks for Remote Data Fetching construindo um aplicativo Pokedex que solicita dados da API Pokemon. Também vamos mergulhar em outros recursos que vêm com o SWR e destacar suas diferenças em comparação com soluções populares, como a API Fetch e a biblioteca Axios, e fornecer as razões para usar essa biblioteca e por que você deve ficar de olho no SWR.
Então, vamos começar respondendo a uma pergunta fundamental: O que é SWR?
O que é SWR?
SWR é um inicialismo de stale-while-revalidate. É uma biblioteca React Hooks para busca remota de dados. O SWR funciona com três etapas principais: primeiro, ele retorna os dados do cache (a parte obsoleta), depois envia a solicitação de busca (a parte de revalidação) e, finalmente, vem com os dados atualizados. Mas não se preocupe, a SWR cuida de todas essas etapas para nós. A única coisa que temos a fazer é fornecer ao gancho useSWR
os parâmetros necessários para fazer a solicitação.
O SWR também possui alguns recursos interessantes, como:
- Agnóstico de back-end
- Navegação rápida da página
- Revalidação em foco
- Pesquisa de intervalo
- Solicitar desduplicação
- Mutação local
- Paginação
- Pronto para TypeScript
- Suporte SSR
- Modo de suspensão
- Suporte nativo do React
- Leve.
Parece mágico? Bem, o SWR simplifica as coisas e aumenta com certeza a experiência do usuário do seu aplicativo React. E assim que começarmos a implementá-lo em nosso projeto, você verá por que esse gancho é útil.
É importante saber que o nome do pacote é swr
ou SWR e o gancho usado para obter os recursos do SWR é chamado useSWR
.
Em teoria, o SWR é talvez o que você precisa para aprimorar sua busca de dados. No entanto, já temos duas ótimas maneiras de fazer solicitações HTTP em nosso aplicativo: a API Fetch e a biblioteca Axios.
Então, por que usar uma nova biblioteca para recuperar dados? vamos tentar responder a essa pergunta legítima na próxima seção.
Comparação com Fetch e Axios
Já temos muitas maneiras de fazer solicitações HTTP em nossos React Apps, e duas das mais populares são a API Fetch e a biblioteca Axios. Ambos são ótimos e nos permitem buscar ou enviar dados facilmente. No entanto, uma vez concluída a operação, eles não nos ajudarão a armazenar em cache ou paginar dados, você terá que fazer isso por conta própria.
Axios ou Fetch apenas tratará da solicitação e retornará a resposta esperada, nada mais.
E comparado ao SWR, é um pouco diferente porque o SWR sob o capô usa a API Fetch para solicitar dados do servidor - é uma espécie de camada construída sobre ele. No entanto, ele possui alguns recursos interessantes, como cache, paginação, recuperação de posição de rolagem, busca dependente, etc. É uma grande vantagem, porque ter esses recursos ajuda a tornar nossos React Apps rápidos e fáceis de usar e reduz significativamente o tamanho do nosso código.
E para concluir, lembre-se de que SWR não é o mesmo que Axios ou Fetch, mesmo que ajude a lidar com solicitações HTTP. O SWR é mais avançado que eles, ele fornece algumas melhorias para manter nosso aplicativo sincronizado com o back-end e, portanto, aumenta o desempenho do nosso aplicativo.
Agora que sabemos quais são as diferenças que o SWR tem em comparação com a biblioteca Axios ou a API Fetch, é hora de mergulhar no porquê de usar essa ferramenta.
Leitura recomendada : Consumindo APIs REST em React With Fetch And Axios
Por que usar SWR para busca de dados?
Como eu disse anteriormente, o SWR vem com alguns recursos úteis que ajudam a aumentar a usabilidade do seu aplicativo facilmente. Com o SWR, você pode paginar seus dados rapidamente usando useSWRPages
, também pode buscar dados que dependem de outra solicitação ou recuperar uma posição de rolagem quando voltar a uma determinada página e muito mais.
Normalmente, mostramos ao usuário uma mensagem de carregamento ou um spinner enquanto buscamos dados do servidor. E com o SWR, você pode melhorar mostrando ao usuário os dados armazenados em cache ou obsoletos enquanto recupera novos dados da API. E assim que essa operação for feita, ele revalidará os dados para mostrar a nova versão. E você não precisa fazer nada, o SWR armazenará os dados em cache na primeira vez que você os buscar e os recuperará automaticamente quando uma nova solicitação for feita.
Até agora, já vimos por que usar SWR em vez de Axios ou Fetch é melhor dependendo obviamente do que você pretende construir. Mas, para muitos casos, recomendarei o uso do SWR porque ele possui ótimos recursos que vão além de apenas buscar e retornar dados.
Dito isso, agora podemos começar a construir nosso aplicativo React e usar a biblioteca SWR para buscar dados remotos.
Então, vamos começar configurando um novo projeto.
Configurando
Como eu disse anteriormente na introdução, vamos construir um aplicativo que busca dados da API Pokemon. Você pode usar uma API diferente se quiser também, vou ficar com ela por enquanto.
E para criar um novo aplicativo, precisamos executar o seguinte comando no terminal:
npx create-react-app react-swr
Em seguida, precisamos instalar a biblioteca SWR navegando primeiro até a pasta que contém o aplicativo React.
cd react-swr
E execute no terminal o seguinte comando para instalar o pacote SWR.
yarn add swr
Ou se você estiver usando npm:
npm install swr
Agora que tudo está configurado, vamos estruturar o projeto da seguinte forma para começar a usar o SWR:
src ├── components | └── Pokemon.js ├── App.js ├── App.test.js ├── index.js ├── serviceWorker.js ├── setupTests.js ├── package.json ├── README.md ├── yarn-error.log └── yarn.lock
Como você pode ver, a estrutura de pastas é simples. A única coisa a notar é a pasta de components
que contém o arquivo Pokemon.js
. Ele será usado posteriormente como um componente de apresentação para mostrar um único Pokémon assim que obtivermos dados da API.
Excelente! Com isso, agora podemos começar a buscar dados da API usando useSWR
.
Buscando dados remotos
O pacote SWR possui alguns recursos úteis, como vimos acima. No entanto, existem duas maneiras de configurar esta biblioteca: localmente ou globalmente.
Uma configuração local significa que toda vez que criamos um novo arquivo, temos que configurar o SWR novamente para poder buscar dados remotos. E uma configuração global nos permite reutilizar uma parte de nossa configuração em arquivos diferentes porque uma função de fetcher
pode ser declarada uma vez e usada em qualquer lugar.
E não se preocupe, veremos ambos neste artigo, mas por enquanto, vamos colocar a mão na massa e adicionar algum código significativo no arquivo App.js
Exibindo os dados
import React from 'react' import useSWR from 'swr' import { Pokemon } from './components/Pokemon' const url = 'https://pokeapi.co/api/v2/pokemon' const fetcher = (...args) => fetch(...args).then((res) => res.json()) function App() { const { data: result, error } = useSWR(url, fetcher) if (error) return <h1>Something went wrong!</h1> if (!result) return <h1>Loading...</h1> return ( <main className='App'> <h1>Pokedex</h1> <div> {result.results.map((pokemon) => ( <Pokemon key={pokemon.name} pokemon={pokemon} /> ))} </div> </main> ) } export default App
Como você pode ver, começamos importando useSWR
da biblioteca SWR. Isso declara o URL da API da qual você deseja obter dados e uma função para buscar esses dados.
A função fetcher
é usada aqui para transformar os dados em JSON. Ele recebe os dados buscados como um argumento e retorna algo.
Observe que aqui, eu uso o operador Rest ( (...args)
) pois não tenho certeza do tipo e tamanho dos dados recebidos como parâmetro, portanto, copio tudo antes de passá-lo novamente como argumento para o fetch
método fornecido pelo useSWR
que transforma os dados em JSON e os retorna.
Dito isso, o fetcher
e a url
da API agora podem ser passados como parâmetros para o hook useSWR
. Com isso, agora ele pode fazer a requisição e retorna dois estados: os dados buscados e um estado de erro. E data: result
é o mesmo que data.result
, usamos a desestruturação de objetos para extrair o result
de data
.
Com os valores retornados, agora podemos verificar se os dados foram buscados com sucesso e, em seguida, percorrê-los. E para cada usuário, use o componente Pokemon para exibi-lo.
Agora que temos os dados e passamos para o Componente Pokemon, é hora de atualizar o Pokemon.js
para poder receber e exibir os dados.
Criando o componente Pokémon
import React from 'react' import useSWR from 'swr' const fetcher = (...args) => fetch(...args).then((res) => res.json()) export const Pokemon = ({ pokemon }) => { const { name } = pokemon const url = 'https://pokeapi.co/api/v2/pokemon/' + name const { data, error } = useSWR(url, fetcher) if (error) return <h1>Something went wrong!</h1> if (!data) return <h1>Loading...</h1> return ( <div className='Card'> <span className='Card--id'>#{data.id}</span> <img className='Card--image' src={data.sprites.front_default} alt={name} /> <h1 className='Card--name'>{name}</h1> <span className='Card--details'> {data.types.map((poke) => poke.type.name).join(', ')} </span> </div> ) }
Aqui, temos um componente que recebe um único dado de Pokémon da API e o exibe. No entanto, os dados recebidos não contêm todos os campos necessários, portanto, temos que fazer outra solicitação à API para obter o objeto Pokemon completo.
E como você pode ver, usamos o mesmo processo para recuperar os dados, mesmo que desta vez adicionemos o nome do Pokémon à URL.
A propósito, se você não está familiarizado com desestruturação, ({ pokemon })
é o mesmo que receber props e acessar o objeto pokemon com props.pokemon
. É apenas um atalho para extrair valores de objetos ou matrizes.
Com isso, se você navegar até a pasta raiz do projeto e executar no terminal o seguinte comando:
yarn start
Ou se você estiver usando npm:
npm start
Você deve ver que os dados são obtidos com sucesso da API do Pokemon e exibidos conforme o esperado.
Excelente! Agora podemos buscar dados remotos com SWR. No entanto, essa configuração é local e pode ser um pouco redundante porque você já pode ver que App.js
e Pokemon.js
usam a mesma função de busca para fazer a mesma coisa.
Mas, felizmente, o pacote vem com um provedor útil chamado SWRConfig
que ajuda a configurar o SWR globalmente. É um componente wrapper que permite que componentes filhos usem a configuração global e, portanto, a função fetcher.
Para configurar o SWR globalmente, precisamos atualizar o arquivo index.js
porque é onde o componente App é renderizado usando o React DOM. Se desejar, você pode usar SWRConfig
diretamente no arquivo App.js
Configurando o SWR globalmente
import React from 'react' import ReactDOM from 'react-dom' import { SWRConfig } from 'swr' import App from './App' import './index.css' const fetcher = (...args) => fetch(...args).then((res) => res.json()) ReactDOM.render( <React.StrictMode> <SWRConfig value={{ fetcher }}> <App /> </SWRConfig> </React.StrictMode>, document.getElementById('root') )
Como você pode ver, começamos importando SWRConfig
que é um provedor que precisa envolver o componente superior ou apenas parte do seu aplicativo React que precisa usar os recursos do SWR. Leva como props um valor que espera um objeto de config. Você pode passar mais de uma propriedade para o objeto de configuração, aqui eu só preciso da função para buscar os dados.
Agora, ao invés de declarar a função fetcher
em cada arquivo, nós a criamos aqui e a passamos como valor para SWRConfig
. Com isso, agora podemos recuperar dados em qualquer nível em nosso aplicativo sem criar outra função e, portanto, evitar redundância.
Além disso, fetcher
é igual a fetcher: fetcher
, é apenas o açúcar sintático proposto pelo ES6. Com essa mudança, precisamos agora atualizar nossos componentes para usar a configuração global.
Usando a configuração global de SWR
import React from 'react' import useSWR from 'swr' import { Pokemon } from './components/Pokemon' const url = 'https://pokeapi.co/api/v2/pokemon' function App() { const { data: result, error } = useSWR(url) if (error) return <h1>Something went wrong!</h1> if (!result) return <h1>Loading...</h1> return ( <main className='App'> <h1>Pokedex</h1> <div> {result.results.map((pokemon) => ( <Pokemon key={pokemon.name} pokemon={pokemon} /> ))} </div> </main> ) } export default App
Agora só precisamos passar a url
para useSWR
, ao invés de passar o método url
e fetcher
. Vamos também ajustar um pouco o componente Pokémon.
import React from 'react' import useSWR from 'swr' export const Pokemon = ({ pokemon }) => { const { name } = pokemon const url = 'https://pokeapi.co/api/v2/pokemon/' + name const { data, error } = useSWR(url) if (error) return <h1>Something went wrong!</h1> if (!data) return <h1>Loading...</h1> return ( <div className='Card'> <span className='Card--id'>#{data.id}</span> <img className='Card--image' src={data.sprites.front_default} alt={name} /> <h1 className='Card--name'>{name}</h1> <span className='Card--details'> {data.types.map((poke) => poke.type.name).join(', ')} </span> </div> ) }
Você já pode ver que não temos mais a função fetcher, graças à configuração global que passa a função para useSWR
sob o capô.
Agora, você pode usar a função de busca global em todos os lugares do seu aplicativo. A única coisa que o gancho useSWR
precisa para buscar dados remotos é a URL.
No entanto, ainda podemos aprimorar a configuração criando um gancho personalizado para evitar declarar a URL repetidamente e, em vez disso, apenas passar como parâmetro o caminho.
Configuração avançada criando um gancho personalizado
Para fazer isso, você deve criar um novo arquivo na raiz do projeto chamado useRequest.js
(você pode nomeá-lo como quiser) e adicionar este bloco de código abaixo a ele.
import useSwr from 'swr' const baseUrl = 'https://pokeapi.co/api/v2' export const useRequest = (path, name) => { if (!path) { throw new Error('Path is required') } const url = name ? baseUrl + path + '/' + name : baseUrl + path const { data, error } = useSwr(url) return { data, error } }
Aqui, temos uma função que recebe um caminho e, opcionalmente, um nome e o anexa à URL base para construir a URL completa. Em seguida, ele verifica se um parâmetro de nome foi recebido ou não e o trata consequentemente.
Em seguida, esse URL é passado como parâmetro para o gancho useSWR
para poder buscar os dados remotos e retorná-los. E se nenhum caminho for passado, ele lançará um erro.
Excelente! precisamos agora ajustar um pouco os componentes para usar nosso gancho personalizado.
import React from 'react' import { useRequest } from './useRequest' import './styles.css' import { Pokemon } from './components/Pokemon' function App() { const { data: result, error } = useRequest('/pokemon') if (error) return <h1>Something went wrong!</h1> if (!result) return <h1>Loading...</h1> return ( <main className='App'> <h1>Pokedex</h1> <div> {result.results.map((pokemon) => ( <Pokemon key={pokemon.name} pokemon={pokemon} /> ))} </div> </main> ) } export default App
Agora, em vez de usar o gancho SWR, usamos o gancho personalizado construído em cima dele e, em seguida, passamos como esperado o caminho como um argumento. Com isso, tudo funcionará como antes, mas com uma configuração muito mais limpa e flexível.
Vamos também atualizar o componente Pokémon.
import React from 'react' import { useRequest } from '../useRequest' export const Pokemon = ({ pokemon }) => { const { name } = pokemon const { data, error } = useRequest('/pokemon', name) if (error) return <h1>Something went wrong!</h1> if (!data) return <h1>Loading...</h1> return ( <div className='Card'> <span className='Card--id'>#{data.id}</span> <img className='Card--image' src={data.sprites.front_default} alt={name} /> <h1 className='Card--name'>{name}</h1> <span className='Card--details'> {data.types.map((poke) => poke.type.name).join(', ')} </span> </div> ) }
Você já pode ver como nosso gancho personalizado torna as coisas mais fáceis e flexíveis. Aqui, só precisamos passar adicionalmente o nome do Pokémon a ser buscado para useRequest
e ele trata de tudo para nós.
Espero que você comece a aproveitar esta biblioteca legal — No entanto, ainda temos coisas para descobrir porque o SWR oferece muitos recursos, e um deles é useSWRPages
que é um gancho para paginar dados facilmente. Então, vamos usar esse gancho no projeto.
Paginar nossos dados com useSWRPages
O SWR nos permite paginar os dados facilmente e solicitar apenas uma parte deles e, quando necessário, buscar novamente os dados para mostrar na próxima página.
Agora, vamos criar um novo arquivo na raiz do projeto usePagination.js
e usá-lo como um gancho personalizado para paginação.
import React from 'react' import useSWR, { useSWRPages } from 'swr' import { Pokemon } from './components/Pokemon' export const usePagination = (path) => { const { pages, isLoadingMore, loadMore, isReachingEnd } = useSWRPages( 'pokemon-page', ({ offset, withSWR }) => { const url = offset || `https://pokeapi.co/api/v2${path}` const { data: result, error } = withSWR(useSWR(url)) if (error) return <h1>Something went wrong!</h1> if (!result) return <h1>Loading...</h1> return result.results.map((pokemon) => ( <Pokemon key={pokemon.name} pokemon={pokemon} /> )) }, (SWR) => SWR.data.next, [] ) return { pages, isLoadingMore, loadMore, isReachingEnd } }
Como você pode ver, aqui começamos importando useSWRPages
que é o auxiliar que permite paginar dados facilmente. Ele recebe 4 argumentos: a chave da requisição pokemon-page
que também é usada para armazenamento em cache, uma função para buscar os dados que retorna um componente se os dados forem recuperados com sucesso, e outra função que pega o objeto SWR
e solicita os dados do próxima página e uma matriz de dependências.
E uma vez que os dados são buscados, a função useSWRPages
retorna vários valores, mas aqui precisamos de 4 deles: as pages
que é o componente retornado com os dados, a função isLoadingMore
que verifica se os dados estão atualmente buscados, a função loadMore
que ajuda na busca mais dados, e o método isReachingEnd
que determina se ainda há dados a serem recuperados ou não.
Agora temos o gancho personalizado que retorna os valores necessários para paginar os dados, agora podemos passar para o arquivo App.js
e ajustá-lo um pouco.
import React from 'react' import { usePagination } from './usePagination' import './styles.css' export default function App() { const { pages, isLoadingMore, loadMore, isReachingEnd } = usePagination( '/pokemon' ) return ( <main className='App'> <h1>Pokedex</h1> <div>{pages}</div> <button onClick={loadMore} disabled={isLoadingMore || isReachingEnd} > Load more... </button> </main> ) }
Depois que o hook usePagination
importado, agora podemos passar o caminho como parâmetro e recuperar os valores retornados. E como as pages
são um componente, não precisamos percorrer os dados ou algo assim.
Em seguida, usamos a função loadMore
no botão para buscar mais dados e desativá-lo se a operação de recuperação não for concluída ou se não houver dados para buscar.
Excelente! com essa alteração, agora podemos navegar na raiz do projeto e iniciar o servidor com este comando para visualizar nosso aplicativo.
yarn start
Ou se você estiver usando npm:
npm start
Você deve ver que os dados foram buscados com sucesso e se você clicar no botão, novos dados serão recuperados pelo SWR.
Até agora, vimos na prática a biblioteca SWR, e espero que você esteja encontrando valor nela. No entanto, ele ainda tem alguns recursos a oferecer. Vamos mergulhar nessas funcionalidades na próxima seção.
Outros recursos do SWR
A biblioteca SWR tem um monte de coisas úteis que simplificam a maneira como criamos aplicativos React.
Revalidação do foco
É um recurso que permite atualizar ou revalidar com precisão os dados ao refocar uma página ou alternar entre as guias. E, por padrão, essa funcionalidade está habilitada, mas você pode desativá-la de qualquer maneira se não atender à sua necessidade. Pode ser útil especialmente se você tiver dados com atualizações de alta frequência.
Recarregar no intervalo
A biblioteca SWR permite buscar novamente os dados após um determinado período de tempo. Pode ser útil quando seus dados mudam em alta velocidade ou você precisa fazer uma nova solicitação para obter uma nova informação do seu banco de dados.
Mutação Local
Com o SWR, você pode definir um estado local temporário que será atualizado automaticamente quando novos dados forem buscados (revalidação). Esse recurso entra em jogo principalmente quando você lida com uma abordagem off-line em primeiro lugar, pois ajuda a atualizar os dados com facilidade.
Recuperação de posição de rolagem
Esse recurso é muito útil, especialmente quando se trata de lidar com listas enormes. Ele permite que você recupere a posição de rolagem depois de voltar à página. E em qualquer caso, aumenta a usabilidade do seu aplicativo.
Busca dependente
SWR permite buscar dados que dependem de outros dados. Isso significa que ele pode buscar os dados A e, uma vez que a operação é concluída, ele os usa para buscar os dados B, evitando cascatas. E esse recurso ajuda quando você tem dados relacionais.
Dito isso, o SWR ajuda a aumentar a experiência do usuário em qualquer assunto. Ele tem mais recursos do que isso e, em muitos casos, é melhor usá-lo na API Fetch ou na biblioteca Axios.
Conclusão
Ao longo deste artigo, vimos por que o SWR é uma biblioteca incrível. Ele permite a busca remota de dados usando React Hooks e ajuda a simplificar alguns recursos avançados prontos para uso, como paginação, armazenamento de dados em cache, nova busca em intervalo, recuperação de posição de rolagem e assim por diante. O SWR também é agnóstico de back-end, o que significa que pode buscar dados de qualquer tipo de API ou banco de dados. Em definitivo, o SWR aumenta muito a experiência do usuário de seus aplicativos React, tem um futuro brilhante e você deve ficar de olho nele ou melhor usá-lo em seu próximo aplicativo React.
Você pode visualizar o projeto finalizado ao vivo aqui.
Obrigado por ler!
Próximos passos
Você pode continuar verificando os links a seguir, que lhe darão uma melhor compreensão além do escopo deste tutorial.
- SWR
- Documentos de SWR
Leitura adicional no SmashingMag:
- Componentes de estilo em React
- Melhores redutores com imersão
- Componentes de ordem superior em React
- Construindo componentes reutilizáveis do React usando o Tailwind