Una introducción a SWR: React Hooks para la obtención remota de datos
Publicado: 2022-03-10SWR 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?
¿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.
¡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.
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