Una introducción a SWR: React Hooks para la obtención remota de datos

Publicado: 2022-03-10
Resumen rápido ↬ En este artículo, veremos una nueva forma de recuperar datos en React Apps llamada SWR. Este es un conjunto de ganchos para la obtención remota de datos que facilita las cosas, como el almacenamiento en caché, la paginación, etc. También construiremos una aplicación Pokedex desde cero y usaremos funciones SWR para obtener datos y paginarlos.

SWR es una biblioteca liviana creada por Vercel (anteriormente ZEIT) que permite obtener, almacenar en caché o recuperar datos en tiempo real utilizando React Hooks. Está construido con React Suspense, que permite que sus componentes "esperen" algo antes de que puedan renderizarse, incluidos los datos. SWR también viene con excelentes funciones, como recuperación dependiente, enfoque en la revalidación, recuperación de posición de desplazamiento, etc. También es una herramienta muy poderosa ya que es independiente del back-end y tiene un buen soporte para TypeScript. Es un paquete que tiene un futuro brillante.

¿Por qué debería importarte? Debería preocuparse si ha estado buscando una biblioteca que no solo obtenga datos de las API, sino que también permita hacer cosas como el almacenamiento en caché y la obtención dependiente. Lo que se cubrirá en este tutorial será útil al crear aplicaciones React con muchas partes móviles. Se espera que haya utilizado Axios y Fetch API, aunque compararemos en qué se diferencian de SWR, no entraremos en detalles sobre cómo se implementarán.

En esta guía, le presentaré React Hooks para la obtención remota de datos mediante la creación de una aplicación Pokedex que solicita datos de la API de Pokémon. También nos sumergiremos en otras características que vienen con SWR, y destacaremos sus diferencias en comparación con soluciones populares como Fetch API y la biblioteca Axios y le daremos las razones por las que usar esta biblioteca y por qué debería estar atento a SWR.

Entonces, comencemos respondiendo una pregunta fundamental: ¿Qué es SWR?

¡Más después del salto! Continúe leyendo a continuación ↓

¿Qué es la ROE?

SWR es una sigla de stale-while-revalidate. Es una biblioteca React Hooks para la obtención remota de datos. SWR funciona con tres pasos principales: primero, devuelve los datos del caché (la parte obsoleta), luego envía la solicitud de recuperación (la parte de revalidación) y finalmente viene con los datos actualizados. Pero no se preocupe, SWR se encarga de todos estos pasos por nosotros. Lo único que tenemos que hacer es darle al gancho useSWR los parámetros necesarios para realizar la solicitud.

SWR también tiene algunas características interesantes como:

  • Agnóstico de back-end
  • Navegación rápida de páginas
  • Revalidación en foco
  • sondeo de intervalo
  • Solicitar deduplicación
  • Mutación local
  • Paginación
  • Preparado para mecanografiado
  • Soporte de SSR
  • Modo de suspenso
  • Reaccionar soporte nativo
  • Ligero.

¿Suena mágico? Bueno, SWR simplifica las cosas y aumenta con seguridad la experiencia del usuario de su aplicación React. Y una vez que comencemos a implementarlo en nuestro proyecto, verá por qué este gancho es útil.

Es importante saber que el nombre del paquete es swr o SWR y que el enlace utilizado para obtener las características de SWR se llama useSWR .

En teoría, SWR es quizás lo que necesita para mejorar la obtención de datos. Sin embargo, ya tenemos dos excelentes formas de realizar solicitudes HTTP en nuestra aplicación: la API Fetch y la biblioteca Axios.

Entonces, ¿por qué usar una nueva biblioteca para recuperar datos? intentemos responder esta pregunta legítima en la siguiente sección.

Comparación con Fetch y Axios

Ya tenemos muchas formas de realizar solicitudes HTTP en nuestras aplicaciones React, y dos de las más populares son la API Fetch y la biblioteca Axios. Ambos son excelentes y nos permiten obtener o enviar datos fácilmente. Sin embargo, una vez realizada la operación, no nos servirán para cachear o paginar datos, hay que hacerlo por nuestra cuenta.

Axios o Fetch solo manejarán la solicitud y devolverán la respuesta esperada, nada más.

Y en comparación con SWR, es un poco diferente porque el SWR debajo del capó usa la API Fetch para solicitar datos del servidor; es una especie de capa construida encima. Sin embargo, tiene algunas características interesantes, como el almacenamiento en caché, la paginación, la recuperación de la posición de desplazamiento, la recuperación dependiente, etc., y para ser precisos, un cierto nivel de reactividad listo para usar que Axios o Fetch no tienen. Es una gran ventaja porque tener tales características ayuda a que nuestras aplicaciones React sean rápidas y fáciles de usar y reducen notablemente el tamaño de nuestro código.

Y para concluir, solo tenga en cuenta que SWR no es lo mismo que Axios o Fetch, incluso si ayuda a manejar las solicitudes HTTP. SWR es más avanzado que ellos, proporciona algunas mejoras para mantener nuestra aplicación sincronizada con el back-end y, por lo tanto, aumenta el rendimiento de nuestra aplicación.

Ahora que sabemos cuáles son las diferencias que tiene SWR en comparación con la biblioteca Axios o la API Fetch, es hora de profundizar en por qué usar dicha herramienta.

Lectura recomendada : Consumo de API REST en React con Fetch y Axios

¿Por qué usar SWR para la obtención de datos?

Como dije anteriormente, SWR viene con algunas características útiles que ayudan a aumentar fácilmente la usabilidad de su aplicación. Con SWR, puede paginar sus datos en poco tiempo usando useSWRPages , también puede obtener datos que dependen de otra solicitud o recuperar una posición de desplazamiento cuando regresa a una página determinada, y mucho más.

Por lo general, le mostramos al usuario un mensaje de carga o una rueda giratoria mientras obtenemos datos del servidor. Y con SWR, puede mejorarlo mostrando al usuario los datos en caché o obsoletos mientras recupera nuevos datos de la API. Y una vez realizada esa operación, revalidará los datos para mostrar la nueva versión. Y no necesita hacer nada, SWR almacenará en caché los datos la primera vez que los obtenga y los recuperará automáticamente cuando se realice una nueva solicitud.

Hasta ahora, ya hemos visto por qué es mejor usar SWR en lugar de Axios o Fetch, dependiendo obviamente de lo que pretenda construir. Pero para muchos casos, recomendaré usar SWR porque tiene excelentes funciones que van más allá de simplemente obtener y devolver datos.

Dicho esto, ahora podemos comenzar a construir nuestra aplicación React y usar la biblioteca SWR para obtener datos remotos.

Entonces, comencemos configurando un nuevo proyecto.

Configuración

Como dije anteriormente en la introducción, crearemos una aplicación que obtenga datos de la API de Pokémon. También puede usar una API diferente si lo desea, me quedaré con ella por ahora.

Y para crear una nueva aplicación, debemos ejecutar el siguiente comando en la terminal:

 npx create-react-app react-swr

A continuación, debemos instalar la biblioteca SWR navegando primero a la carpeta que contiene la aplicación React.

 cd react-swr

Y ejecute en la terminal el siguiente comando para instalar el paquete SWR.

 yarn add swr

O si está usando npm:

 npm install swr

Ahora que hemos configurado todo, estructuremos el proyecto de la siguiente manera para comenzar a usar 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 puede ver, la estructura de carpetas es simple. Lo único que se debe notar es la carpeta de components que contiene el archivo Pokemon.js . Se usará más adelante como un componente de presentación para mostrar un solo Pokémon una vez que obtengamos datos de la API.

¡Genial! Con eso en su lugar, ahora podemos comenzar a obtener datos de la API usando useSWR .

Obtención de datos remotos

El paquete SWR tiene algunas características útiles como hemos visto anteriormente. Sin embargo, hay dos formas de configurar esta biblioteca: localmente o globalmente.

Una configuración local significa que cada vez que creamos un nuevo archivo, tenemos que configurar SWR nuevamente para poder obtener datos remotos. Y una configuración global nos permite reutilizar una parte de nuestra configuración dentro de diferentes archivos porque una función de fetcher puede declararse una vez y usarse en todas partes.

Y no se preocupe, veremos ambos en este artículo, pero por ahora, ensuciémonos las manos y agreguemos un código significativo en el archivo App.js

Visualización de los datos

 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 puede ver, comenzamos importando useSWR de la biblioteca SWR. Esto declara la URL de la API de la que desea obtener datos y una función para obtener estos datos.

La función fetcher se utiliza aquí para transformar los datos en JSON. Recibe los datos obtenidos como argumento y devuelve algo.

Tenga en cuenta que aquí uso el operador Rest ( (...args) ) ya que no estoy seguro del tipo y la longitud de los datos recibidos como parámetro, por lo tanto, copio todo antes de pasarlo nuevamente como argumento para la fetch método proporcionado por useSWR que transforma los datos en JSON y los devuelve.

Dicho esto, el fetcher y la url de la API ahora se pueden pasar como parámetros al gancho useSWR . Con eso, ahora puede realizar la solicitud y devuelve dos estados: los datos obtenidos y un estado de error. Y data: result es lo mismo que data.result , usamos la desestructuración de objetos para extraer el result de data .

Con los valores devueltos, ahora podemos verificar si los datos se obtuvieron con éxito y luego recorrerlos. Y para cada usuario, use el componente Pokemon para mostrarlo.

Ahora que tenemos los datos y los pasamos al componente Pokémon, es hora de actualizar Pokemon.js para poder recibir y mostrar los datos.

Creando el 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> ) }

Aquí, tenemos un componente que recibe un solo dato de Pokémon de la API y lo muestra. Sin embargo, los datos recibidos no contienen todos los campos necesarios, por lo que debemos realizar otra solicitud a la API para obtener el objeto Pokémon completo.

Y como puede ver, usamos el mismo proceso para recuperar los datos, incluso si esta vez agregamos el nombre del Pokémon a la URL.

Por cierto, si no estás familiarizado con la desestructuración, ({ pokemon }) es lo mismo que recibir accesorios y acceder al objeto pokemon con props.pokemon . Es solo una abreviatura para extraer valores de objetos o matrices.

Con eso en su lugar, si navega a la carpeta raíz del proyecto y ejecuta en la terminal el siguiente comando:

 yarn start

O si está usando npm:

 npm start

Debería ver que los datos se obtienen con éxito de la API de Pokémon y se muestran como se esperaba.

atractivo
Obteniendo ilustración. (Vista previa grande)

¡Genial! Ahora podemos obtener datos remotos con SWR. Sin embargo, esta configuración es local y puede ser un poco redundante porque ya puedes ver que App.js y Pokemon.js usan la misma función de búsqueda para hacer lo mismo.

Pero afortunadamente, el paquete viene con un práctico proveedor llamado SWRConfig que ayuda a configurar SWR globalmente. Es un componente contenedor que permite que los componentes secundarios usen la configuración global y, por lo tanto, la función de búsqueda.

Para configurar SWR globalmente, necesitamos actualizar el archivo index.js porque es donde se procesa el componente de la aplicación usando React DOM. Si lo desea, puede usar SWRConfig directamente en el archivo App.js

Configuración de 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 puede ver, comenzamos importando SWRConfig que es un proveedor que necesita envolver el componente superior o solo una parte de su aplicación React que necesita usar las funciones de SWR. Toma como accesorios un valor que espera un objeto de configuración. Puede pasar más de una propiedad al objeto de configuración, aquí solo necesito la función para obtener datos.

Ahora, en lugar de declarar la función de fetcher en cada archivo, la creamos aquí y la pasamos como valor a SWRConfig . Con eso, ahora podemos recuperar datos en cualquier nivel de nuestra aplicación sin crear otra función y, por lo tanto, evitar la redundancia.

Además de eso, fetcher es igual a fetcher: fetcher , es solo azúcar sintáctico propuesto por ES6. Con ese cambio, ahora necesitamos actualizar nuestros componentes para usar la configuración global.

Uso de la configuración 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

Ahora solo necesitamos pasar la url a useSWR , en lugar de pasar la url y el método de fetcher . También modifiquemos un poco el 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> ) }

Ya puede ver que ya no tenemos la función de búsqueda, gracias a la configuración global que pasa la función a useSWR bajo el capó.

Ahora, puede usar la función de búsqueda global en cualquier parte de su aplicación. Lo único que necesita el useSWR para obtener datos remotos es la URL.

Sin embargo, aún podemos mejorar aún más la configuración creando un enlace personalizado para evitar declarar la URL una y otra vez y, en su lugar, simplemente pasar como parámetro la ruta.

Configuración avanzada mediante la creación de un gancho personalizado

Para hacerlo, debe crear un nuevo archivo en la raíz del proyecto llamado useRequest.js (puede nombrarlo como desee) y agregarle este bloque de código a continuación.

 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 } }

Aquí, tenemos una función que recibe una ruta y, opcionalmente, un nombre y lo agrega a la URL base para construir la URL completa. A continuación, comprueba si se recibe o no un parámetro de nombre y lo gestiona en consecuencia.

Luego, esa URL se pasa como un parámetro al useSWR para poder obtener los datos remotos y devolverlos. Y si no se pasa ninguna ruta, arroja un error.

¡Genial! ahora necesitamos modificar un poco los componentes para usar nuestro enlace 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

Ahora, en lugar de usar el enlace SWR, usamos el enlace personalizado construido sobre él y luego pasamos la ruta como un argumento, como se esperaba. Con eso en su lugar, todo funcionará como antes pero con una configuración mucho más limpia y flexible.

Actualicemos también el 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> ) }

Ya puedes ver cómo nuestro gancho personalizado hace las cosas más fáciles y flexibles. Aquí, solo necesitamos pasar adicionalmente el nombre del Pokémon para buscar a useRequest y se encargará de todo por nosotros.

Espero que empieces a disfrutar de esta genial biblioteca. Sin embargo, todavía tenemos cosas por descubrir porque SWR ofrece muchas funciones, y una de ellas es useSWRPages que es un gancho para paginar datos fácilmente. Entonces, usemos ese gancho en el proyecto.

Paginar nuestros datos con useSWRPages

SWR nos permite paginar datos fácilmente y solicitar solo una parte de ellos, y cuando sea necesario recuperar datos para mostrarlos en la página siguiente.

Ahora, vamos a crear un nuevo archivo en la raíz del proyecto usePagination.js y usarlo como enlace personalizado para la paginación.

 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 puede ver, aquí comenzamos importando useSWRPages que es el ayudante que permite paginar datos fácilmente. Recibe 4 argumentos: la clave de la pokemon-page de solicitud que también se utiliza para el almacenamiento en caché, una función para recuperar los datos que devuelve un componente si los datos se recuperan con éxito, y otra función que toma el objeto SWR y solicita datos del página siguiente y una serie de dependencias.

Y una vez que se han obtenido los datos, la función useSWRPages devuelve varios valores, pero aquí necesitamos 4 de ellos: las pages que son el componente devuelto con los datos, la función isLoadingMore que comprueba si los datos se han obtenido actualmente, la función loadMore que ayuda a obtener más datos y el método isReachingEnd que determina si todavía hay datos para recuperar o no.

Ahora que tenemos el enlace personalizado que devuelve los valores necesarios para paginar los datos, ahora podemos movernos al archivo App.js y modificarlo un poco.

 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> ) }

Una vez que se importó el usePagination , ahora podemos pasar la ruta como un parámetro y recuperar los valores devueltos. Y dado que pages son un componente, no necesitamos recorrer los datos ni nada por el estilo.

A continuación, usamos la función loadMore en el botón para obtener más datos y desactivarlo si la operación de recuperación no ha finalizado o si no hay datos para obtener.

¡Genial! con ese cambio, ahora podemos navegar en la raíz del proyecto e iniciar el servidor con este comando para obtener una vista previa de nuestra aplicación.

 yarn start

O si está usando npm:

 npm start

Debería ver que los datos se obtuvieron con éxito y si hace clic en el botón, SWR recuperará nuevos datos.

Paginación
Paginación. (Vista previa grande)

Hasta ahora, hemos visto en la práctica la biblioteca SWR, y espero que le encuentre valor. Sin embargo, todavía tiene algunas características que ofrecer. Profundicemos en estas funcionalidades en la siguiente sección.

Otras características de SWR

La biblioteca SWR tiene un montón de cosas útiles que simplifican la forma en que creamos aplicaciones React.

Revalidación de enfoque

Es una función que permite actualizar o revalidar para ser precisos los datos cuando vuelves a enfocar una página o cambias de pestaña. Y de forma predeterminada, esta funcionalidad está habilitada, pero puede deshabilitarla de todos modos si no se ajusta a sus necesidades. Puede ser útil especialmente si tiene datos con actualizaciones de alta frecuencia.

Recuperar en intervalo

La biblioteca SWR permite recuperar datos después de un cierto período de tiempo. Puede ser útil cuando sus datos cambian a gran velocidad o necesita realizar una nueva solicitud para obtener nueva información de su base de datos.

mutación local

Con SWR, puede establecer un estado local temporal que se actualizará automáticamente cuando se obtengan nuevos datos (revalidación). Esta característica entra en juego particularmente cuando se trata de un enfoque sin conexión primero, ayuda a actualizar los datos fácilmente.

Recuperación de posición de desplazamiento

Esta función es muy útil, especialmente cuando se trata de listas enormes. Le permite recuperar la posición de desplazamiento después de volver a la página. Y en cualquier caso, aumenta la usabilidad de tu app.

Obtención dependiente

SWR le permite obtener datos que dependen de otros datos. Eso significa que puede obtener los datos A, y una vez que se realiza la operación, los usa para obtener los datos B mientras evita las cascadas. Y esta característica ayuda cuando tienes datos relacionales.

Dicho esto, SWR ayuda a aumentar la experiencia del usuario en cualquier asunto. Tiene más funciones que eso y, en muchos casos, es mejor usarlo sobre la API Fetch o la biblioteca Axios.

Conclusión

A lo largo de este artículo, hemos visto por qué SWR es una biblioteca increíble. Permite la recuperación remota de datos mediante React Hooks y ayuda a simplificar algunas funciones avanzadas listas para usar, como la paginación, el almacenamiento en caché de datos, la recuperación en intervalos, la recuperación de la posición de desplazamiento, etc. SWR también es independiente del back-end, lo que significa que puede obtener datos de cualquier tipo de API o base de datos. En definitiva, SWR aumenta mucho la experiencia de usuario de tus aplicaciones React, tiene un futuro brillante y deberías estar atento o mejor utilizarlo en tu próxima aplicación React.

Puede obtener una vista previa del proyecto terminado en vivo aquí.

¡Gracias por leer!

Próximos pasos

Puede continuar revisando los siguientes enlaces que le darán una mejor comprensión más allá del alcance de este tutorial.

  • ROE
  • Documentos de ROE

Lectura adicional en SmashingMag:

  • Estilizar componentes en React
  • Mejores reductores con Immer
  • Componentes de orden superior en React
  • Creación de componentes React reutilizables con Tailwind