Obtención de datos obsoletos durante la revalidación con ganchos de React: una guía

Publicado: 2022-03-11

Aprovechar la extensión HTTP Cache-Control obsoleta mientras se revalida es una técnica popular. Implica el uso de activos almacenados en caché (obsoletos) si se encuentran en el caché, y luego revalidar el caché y actualizarlo con una versión más nueva del activo si es necesario. De ahí el nombre stale-while-revalidate .

Cómo funciona stale-while-revalidate

Cuando se envía una solicitud por primera vez, el navegador la almacena en caché. Luego, cuando se envía la misma solicitud por segunda vez, primero se verifica el caché. Si la memoria caché de esa solicitud está disponible y es válida, la memoria caché se devuelve como respuesta. A continuación, se comprueba si la memoria caché está obsoleta y se actualiza si se encuentra obsoleta. La obsolescencia de un caché está determinada por el valor max-age presente en el encabezado Cache-Control junto con stale-while-revalidate .

Un diagrama de flujo que rastrea la lógica obsoleta mientras se revalida. Comienza con una petición. Si no está en caché, o si el caché no es válido, se envía la solicitud, se devuelve la respuesta y se actualiza el caché. De lo contrario, se devuelve la respuesta almacenada en caché, después de lo cual se comprueba si la caché está obsoleta. Si está obsoleto, se envía una solicitud y se actualiza la memoria caché.

Esto permite cargas de página rápidas, ya que los activos almacenados en caché ya no se encuentran en la ruta crítica. Se cargan al instante. Además, dado que los desarrolladores controlan la frecuencia con la que se usa y actualiza el caché, pueden evitar que los navegadores muestren datos demasiado obsoletos a los usuarios.

Los lectores podrían estar pensando que, si pueden hacer que el servidor use ciertos encabezados en sus respuestas y dejar que el navegador los tome a partir de ahí, ¿cuál es la necesidad de usar React y Hooks para el almacenamiento en caché?

Resulta que el enfoque de servidor y navegador solo funciona bien cuando queremos almacenar en caché contenido estático. ¿Qué pasa con el uso stale-while-revalidate para una API dinámica? Es difícil encontrar buenos valores para max-age y stale-while-revalidate en ese caso. A menudo, invalidar el caché y obtener una respuesta nueva cada vez que se envía una solicitud será la mejor opción. Esto significa efectivamente que no hay almacenamiento en caché. Pero con React y Hooks, podemos hacerlo mejor.

stale-while-revalidate para la API

Nos dimos cuenta de que la stale-while-revalidate de HTTP no funciona bien con solicitudes dinámicas como llamadas a la API.

Incluso si terminamos usándolo, el navegador devolverá el caché o la respuesta nueva, no ambos. Esto no va bien con una solicitud de API, ya que nos gustaría recibir respuestas nuevas cada vez que se envía una solicitud. Sin embargo, esperar nuevas respuestas retrasa la usabilidad significativa de la aplicación.

¿Asi que que hacemos?

Implementamos un mecanismo de almacenamiento en caché personalizado. Dentro de eso, encontramos una forma de devolver tanto el caché como la respuesta nueva. En la interfaz de usuario, la respuesta almacenada en caché se reemplaza con una respuesta nueva cuando está disponible. Así es como se vería la lógica:

  1. Cuando se envía una solicitud al extremo del servidor API por primera vez, almacene en caché la respuesta y luego devuélvala.
  2. La próxima vez que ocurra la misma solicitud de API, use la respuesta almacenada en caché de inmediato.
  3. Luego, envíe la solicitud de forma asíncrona para obtener una nueva respuesta. Cuando llegue la respuesta, propague de forma asíncrona los cambios a la interfaz de usuario y actualice la memoria caché.

Este enfoque permite actualizaciones instantáneas de la interfaz de usuario, ya que cada solicitud de la API se almacena en caché, pero también una eventual corrección en la interfaz de usuario, ya que los datos de respuesta actualizados se muestran tan pronto como están disponibles.

En este tutorial, veremos un enfoque paso a paso sobre cómo implementar esto. Llamaremos a este enfoque obsoleto mientras se actualiza, ya que la interfaz de usuario se actualiza cuando recibe la respuesta nueva.

Preparativos: La API

Para iniciar este tutorial, primero necesitaremos una API de la que obtengamos datos. Afortunadamente, hay un montón de servicios de API simulados disponibles. Para este tutorial, usaremos reqres.in.

Los datos que obtenemos son una lista de usuarios con un parámetro de consulta de page . Así es como se ve el código de recuperación:

 fetch("https://reqres.in/api/users?page=2") .then(res => res.json()) .then(json => { console.log(json); });

Ejecutar este código nos da el siguiente resultado. Aquí hay una versión no repetitiva de esto:

 { page: 2, per_page: 6, total: 12, total_pages: 2, data: [ { id: 7, email: "[email protected]", first_name: "Michael", last_name: "Lawson", avatar: "https://s3.amazonaws.com/uifaces/faces/twitter/follettkyle/128.jpg" }, // 5 more items ] }

Puedes ver que esto es como una API real. Tenemos paginación en la respuesta. El parámetro de consulta de page es responsable de cambiar la página y tenemos un total de dos páginas en el conjunto de datos.

Usando la API en una aplicación React

Veamos cómo usamos la API en una aplicación React. Una vez que sepamos cómo hacerlo, resolveremos la parte del almacenamiento en caché. Usaremos una clase para crear nuestro componente. Aquí está el código:

 import React from "react"; import PropTypes from "prop-types"; export default class Component extends React.Component { state = { users: [] }; componentDidMount() { this.load(); } load() { fetch(`https://reqres.in/api/users?page=${this.props.page}`) .then(res => res.json()) .then(json => { this.setState({ users: json.data }); }); } componentDidUpdate(prevProps) { if (prevProps.page !== this.props.page) { this.load(); } } render() { const users = this.state.users.map(user => ( <p key={user.id}> <img src={user.avatar} alt={user.first_name} style={{ height: 24, width: 24 }} /> {user.first_name} {user.last_name} </p> )); return <div>{users}</div>; } } Component.propTypes = { page: PropTypes.number.isRequired };

Tenga en cuenta que estamos obteniendo el valor de la page a través de props , como sucede a menudo en las aplicaciones del mundo real. Además, tenemos una función componentDidUpdate , que recupera los datos de la API cada vez que cambia this.props.page .

En este punto, muestra una lista de seis usuarios porque la API devuelve seis elementos por página:

Una vista previa de nuestro prototipo de componente React: seis líneas centradas, cada una con una foto a la izquierda de un nombre.

Adición de almacenamiento en caché obsoleto mientras se actualiza

Si queremos agregar almacenamiento en caché obsoleto mientras se actualiza a esto, debemos actualizar la lógica de nuestra aplicación a:

  1. Guarde en caché la respuesta de una solicitud de forma única después de que se obtenga por primera vez.
  2. Devuelve la respuesta almacenada en caché al instante si se encuentra el caché de una solicitud. Luego, envíe la solicitud y devuelva la respuesta nueva de forma asíncrona. Además, almacene en caché esta respuesta para la próxima vez.

Podemos hacer esto al tener un objeto CACHE global que almacene el caché de forma única. Por singularidad, podemos usar el valor this.props.page como clave en nuestro objeto CACHE . Luego, simplemente codificamos el algoritmo mencionado anteriormente.

 import apiFetch from "./apiFetch"; const CACHE = {}; export default class Component extends React.Component { state = { users: [] }; componentDidMount() { this.load(); } load() { if (CACHE[this.props.page] !== undefined) { this.setState({ users: CACHE[this.props.page] }); } apiFetch(`https://reqres.in/api/users?page=${this.props.page}`).then( json => { CACHE[this.props.page] = json.data; this.setState({ users: json.data }); } ); } componentDidUpdate(prevProps) { if (prevProps.page !== this.props.page) { this.load(); } } render() { // same render code as above } }

Dado que el caché se devuelve tan pronto como se encuentra y que setState también devuelve los nuevos datos de respuesta, esto significa que tenemos actualizaciones de interfaz de usuario perfectas y no más tiempo de espera en la aplicación desde la segunda solicitud en adelante. Esto es perfecto, y es el método obsoleto mientras se actualiza en pocas palabras.

Un diagrama de flujo que rastrea la lógica obsoleta mientras se actualiza. Comienza con una petición. Si está en caché, se llama a setState() con la respuesta en caché. De cualquier manera, se envía la solicitud, se establece el caché y se llama a setState() con una respuesta nueva.

La función apiFetch aquí no es más que un contenedor sobre la fetch para que podamos ver la ventaja del almacenamiento en caché en tiempo real. Para ello, agrega un usuario aleatorio a la lista de users devueltos por la solicitud de la API. También le agrega un retraso aleatorio:

 export default async function apiFetch(...args) { await delay(Math.ceil(400 + Math.random() * 300)); const res = await fetch(...args); const json = await res.json(); json.data.push(getFakeUser()); return json; } function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }

La función getFakeUser() aquí es responsable de crear un objeto de usuario falso.

Con estos cambios, nuestra API es más real que antes.

  1. Tiene un retraso aleatorio en la respuesta.
  2. Devuelve datos ligeramente diferentes para las mismas solicitudes.

Dado esto, cuando cambiamos la propiedad de la page pasada al Component desde nuestro componente principal, podemos ver el almacenamiento en caché de la API en acción. Intente hacer clic en el botón Alternar una vez cada pocos segundos en este CodeSandbox y debería ver un comportamiento como este:

Una animación que muestra la página de alternancia con el almacenamiento en caché habilitado. Los detalles se describen en el artículo.

Si miras de cerca, suceden algunas cosas.

  1. Cuando la aplicación se inicia y está en su estado predeterminado, vemos una lista de siete usuarios. Tome nota del último usuario de la lista, ya que es el usuario que se modificará aleatoriamente la próxima vez que se envíe esta solicitud.
  2. Cuando hacemos clic en Alternar por primera vez, espera una pequeña cantidad de tiempo (400-700 ms) y luego actualiza la lista a la página siguiente.
  3. Ahora, estamos en la segunda página. De nuevo, tome nota del último usuario de la lista.
  4. Ahora, volvemos a hacer clic en Alternar y la aplicación volverá a la primera página. Observe que ahora la última entrada sigue siendo el mismo usuario que anotamos en el Paso 1, y luego cambia al nuevo usuario (aleatorio). Esto se debe a que, inicialmente, se mostraba el caché y luego se activaba la respuesta real.
  5. Hacemos clic en Toggle nuevamente. Ocurre el mismo fenómeno. La respuesta almacenada en caché de la última vez se carga instantáneamente, y luego se obtienen nuevos datos, por lo que vemos la última actualización de entrada de lo que anotamos en el Paso 3.

Esto es todo, el almacenamiento en caché obsoleto mientras se actualiza que estábamos buscando. Pero este enfoque adolece de un problema de duplicación de código. Veamos cómo funciona si tenemos otro componente de obtención de datos con almacenamiento en caché. Este componente muestra los elementos de manera diferente a nuestro primer componente.

Agregar obsoleto mientras se actualiza a otro componente

Podemos hacer esto simplemente copiando la lógica del primer componente. Nuestro segundo componente muestra una lista de gatos:

 const CACHE = {}; export default class Component2 extends React.Component { state = { cats: [] }; componentDidMount() { this.load(); } load() { if (CACHE[this.props.page] !== undefined) { this.setState({ cats: CACHE[this.props.page] }); } apiFetch(`https://reqres.in/api/cats?page=${this.props.page}`).then( json => { CACHE[this.props.page] = json.data; this.setState({ cats: json.data }); } ); } componentDidUpdate(prevProps) { if (prevProps.page !== this.props.page) { this.load(); } } render() { const cats = this.state.cats.map(cat => ( <p key={cat.id} style={{ background: cat.color, padding: "4px", width: 240 }} > {cat.name} (born {cat.year}) </p> )); return <div>{cats}</div>; } }

Como puede ver, la lógica del componente involucrada aquí es más o menos la misma que la del primer componente. La única diferencia está en el punto final solicitado y que muestra los elementos de la lista de manera diferente.

Ahora, mostramos estos dos componentes uno al lado del otro. Puedes ver que se comportan de manera similar:

Una animación que muestra la alternancia con dos componentes uno al lado del otro.

Para lograr este resultado, tuvimos que duplicar mucho el código. Si tuviéramos varios componentes como este, estaríamos duplicando demasiado código.

Para resolverlo de una manera que no se duplique, podemos tener un componente de orden superior para obtener y almacenar datos en caché y transmitirlos como accesorios. No es lo ideal, pero funcionará. Pero si tuviéramos que hacer múltiples solicitudes en un solo componente, tener múltiples componentes de orden superior se volvería feo muy rápidamente.

Luego, tenemos el patrón render props, que es probablemente la mejor manera de hacer esto en componentes de clase. Funciona a la perfección, pero, de nuevo, es propenso al "infierno de envoltura" y requiere que nos vinculemos al contexto actual a veces. Esta no es una gran experiencia para los desarrolladores y puede generar frustración y errores.

Aquí es donde React Hooks salva el día. Nos permiten empaquetar la lógica del componente en un contenedor reutilizable para que podamos usarlo en múltiples lugares. Los React Hooks se introdujeron en React 16.8 y solo funcionan con componentes de función. Antes de llegar al control de caché de React, en particular, al almacenamiento en caché de contenido con Hooks, primero veamos cómo hacemos la obtención de datos simple en componentes de función.

Obtención de datos API en componentes de función

Para obtener datos de la API en los componentes de funciones, usamos ganchos useState y useEffect .

useState es análogo al state de los componentes de clase y setState . Usamos este enlace para tener contenedores atómicos de estado dentro de un componente de función.

useEffect es un enlace de ciclo de vida, y puede considerarlo como una combinación de componentDidMount , componentDidUpdate y componentWillUnmount . El segundo parámetro que se pasa a useEffect se denomina matriz de dependencia. Cuando cambia la matriz de dependencia, la devolución de llamada pasada como primer argumento a useEffect se vuelve a ejecutar.

Así es como usaremos estos ganchos para implementar la obtención de datos:

 import React, { useState, useEffect } from "react"; export default function Component({ page }) { const [users, setUsers] = useState([]); useEffect(() => { fetch(`https://reqres.in/api/users?page=${page}`) .then(res => res.json()) .then(json => { setUsers(json.data); }); }, [page]); const usersDOM = users.map(user => ( <p key={user.id}> <img src={user.avatar} alt={user.first_name} style={{ height: 24, width: 24 }} /> {user.first_name} {user.last_name} </p> )); return <div>{usersDOM}</div>; }

Al especificar la page como una dependencia para useEffect , le indicamos a React que ejecute nuestra devolución de llamada useEffect cada vez que se cambia la page . Esto es como componentDidUpdate . Además, useEffect siempre se ejecuta la primera vez, por lo que también funciona como componentDidMount .

Obsoleto mientras se actualiza en componentes de función

Sabemos que useEffect es similar a los métodos del ciclo de vida de los componentes. Entonces, podemos modificar la función de devolución de llamada que se le pasó para crear el almacenamiento en caché obsoleto mientras se actualiza que teníamos en los componentes de clase. Todo permanece igual excepto el gancho useEffect .

 const CACHE = {}; export default function Component({ page }) { const [users, setUsers] = useState([]); useEffect(() => { if (CACHE[page] !== undefined) { setUsers(CACHE[page]); } apiFetch(`https://reqres.in/api/users?page=${page}`).then(json => { CACHE[page] = json.data; setUsers(json.data); }); }, [page]); // ... create usersDOM from users return <div>{usersDOM}</div>; }

Por lo tanto, tenemos un almacenamiento en caché obsoleto mientras se actualiza trabajando en un componente de función.

Podemos hacer lo mismo para el segundo componente, es decir, convertirlo para que funcione e implementar el almacenamiento en caché obsoleto mientras se actualiza. El resultado será idéntico al que teníamos en las clases.

Pero eso no es mejor que los componentes de clase, ¿verdad? Entonces, veamos cómo podemos usar el poder de un enlace personalizado para crear una lógica modular obsoleta mientras se actualiza que podemos usar en múltiples componentes.

Un gancho personalizado obsoleto mientras se actualiza

Primero, reduzcamos la lógica que queremos mover a un gancho personalizado. Si observa el código anterior, sabrá que es la parte useState y useEffect . Más específicamente, esta es la lógica que queremos modularizar.

 const [users, setUsers] = useState([]); useEffect(() => { if (CACHE[page] !== undefined) { setUsers(CACHE[page]); } apiFetch(`https://reqres.in/api/users?page=${page}`).then(json => { CACHE[page] = json.data; setUsers(json.data); }); }, [page]);

Como tenemos que hacerlo genérico, tendremos que hacer que la URL sea dinámica. Entonces necesitamos tener url como argumento. También necesitaremos actualizar la lógica de almacenamiento en caché, ya que varias solicitudes pueden tener el mismo valor page . Afortunadamente, cuando la page se incluye con la URL del punto final, genera un valor único para cada solicitud única. Entonces, podemos usar la URL completa como clave para el almacenamiento en caché:

 const [data, setData] = useState([]); useEffect(() => { if (CACHE[url] !== undefined) { setData(CACHE[url]); } apiFetch(url).then(json => { CACHE[url] = json.data; setData(json.data); }); }, [url]);

Eso es practicamente todo. Después de envolverlo dentro de una función, tendremos nuestro enlace personalizado. Echa un vistazo a continuación.

 const CACHE = {}; export default function useStaleRefresh(url, defaultValue = []) { const [data, setData] = useState(defaultValue); useEffect(() => { // cacheID is how a cache is identified against a unique request const cacheID = url; // look in cache and set response if present if (CACHE[cacheID] !== undefined) { setData(CACHE[cacheID]); } // fetch new data apiFetch(url).then(newData => { CACHE[cacheID] = newData.data; setData(newData.data); }); }, [url]); return data; }

Observe que le hemos agregado otro argumento llamado defaultValue . El valor predeterminado de una llamada API puede ser diferente si usa este enlace en varios componentes. Es por eso que lo hemos hecho personalizable.

Se puede hacer lo mismo para la clave de data en el objeto newData . Si su enlace personalizado devuelve una variedad de datos, es posible que desee devolver newData y no newData.data y manejar ese recorrido en el lado del componente.

Ahora que tenemos nuestro enlace personalizado, que hace el trabajo pesado del almacenamiento en caché obsoleto mientras se actualiza, así es como lo conectamos a nuestros componentes. Observe la gran cantidad de código que pudimos reducir. Todo nuestro componente ahora es solo tres declaraciones. Esa es una gran victoria.

 import useStaleRefresh from "./useStaleRefresh"; export default function Component({ page }) { const users = useStaleRefresh(`https://reqres.in/api/users?page=${page}`, []); const usersDOM = users.map(user => ( <p key={user.id}> <img src={user.avatar} alt={user.first_name} style={{ height: 24, width: 24 }} /> {user.first_name} {user.last_name} </p> )); return <div>{usersDOM}</div>; }

Podemos hacer lo mismo para el segundo componente. Se verá así:

 export default function Component2({ page }) { const cats = useStaleRefresh(`https://reqres.in/api/cats?page=${page}`, []); // ... create catsDOM from cats return <div>{catsDOM}</div>; }

Es fácil ver cuánto código repetitivo podemos ahorrar si usamos este gancho. El código también se ve mejor. Si desea ver la aplicación completa en acción, diríjase a este CodeSandbox.

Agregar un indicador de carga para useStaleRefresh

Ahora que tenemos los conceptos básicos en el punto, podemos agregar más funciones a nuestro enlace personalizado. Por ejemplo, podemos agregar un valor de isLoading en el gancho que sea verdadero cada vez que se envíe una solicitud única y no tengamos ningún caché para mostrar mientras tanto.

Hacemos esto al tener un estado separado para isLoading y configurarlo de acuerdo con el estado del gancho. Es decir, cuando no hay contenido web en caché disponible, lo establecemos en true ; de lo contrario, lo establecemos en false .

Aquí está el gancho actualizado:

 export default function useStaleRefresh(url, defaultValue = []) { const [data, setData] = useState(defaultValue); const [isLoading, setLoading] = useState(true); useEffect(() => { // cacheID is how a cache is identified against a unique request const cacheID = url; // look in cache and set response if present if (CACHE[cacheID] !== undefined) { setData(CACHE[cacheID]); setLoading(false); } else { // else make sure loading set to true setLoading(true); } // fetch new data apiFetch(url).then(newData => { CACHE[cacheID] = newData.data; setData(newData.data); setLoading(false); }); }, [url]); return [data, isLoading]; }

Ahora podemos usar el nuevo valor isLoading en nuestros componentes.

 export default function Component({ page }) { const [users, isLoading] = useStaleRefresh( `https://reqres.in/api/users?page=${page}`, [] ); if (isLoading) { return <div>Loading</div>; } // ... create usersDOM from users return <div>{usersDOM}</div>; }

Tenga en cuenta que una vez hecho esto, verá el texto "Cargando" cuando se envíe una solicitud única por primera vez y no haya caché presente.

Una animación que muestra el componente con un indicador de carga implementado.

Hacer que useStaleRefresh compatible con cualquier función async

Podemos hacer que nuestro enlace personalizado sea aún más poderoso al hacer que admita cualquier función async en lugar de solo solicitudes de red GET . La idea básica detrás de esto seguirá siendo la misma.

  1. En el gancho, llamas a una función asíncrona que devuelve un valor después de un tiempo.
  2. Cada llamada única a una función asíncrona se almacena correctamente en caché.

Una simple concatenación de function.name y arguments funcionará como una clave de caché para nuestro caso de uso. Usando eso, así es como se verá nuestro gancho:

 import { useState, useEffect, useRef } from "react"; import isEqual from "lodash/isEqual"; const CACHE = {}; export default function useStaleRefresh(fn, args, defaultValue = []) { const prevArgs = useRef(null); const [data, setData] = useState(defaultValue); const [isLoading, setLoading] = useState(true); useEffect(() => { // args is an object so deep compare to rule out false changes if (isEqual(args, prevArgs.current)) { return; } // cacheID is how a cache is identified against a unique request const cacheID = hashArgs(fn.name, ...args); // look in cache and set response if present if (CACHE[cacheID] !== undefined) { setData(CACHE[cacheID]); setLoading(false); } else { // else make sure loading set to true setLoading(true); } // fetch new data fn(...args).then(newData => { CACHE[cacheID] = newData; setData(newData); setLoading(false); }); }, [args, fn]); useEffect(() => { prevArgs.current = args; }); return [data, isLoading]; } function hashArgs(...args) { return args.reduce((acc, arg) => stringify(arg) + ":" + acc, ""); } function stringify(val) { return typeof val === "object" ? JSON.stringify(val) : String(val); }

Como puede ver, estamos usando una combinación de nombre de función y sus argumentos en forma de cadena para identificar de forma única una llamada de función y, por lo tanto, almacenarla en caché. Esto funciona para nuestra aplicación simple, pero este algoritmo es propenso a colisiones y comparaciones lentas. (Con argumentos que no se pueden serializar, no funcionará en absoluto). Por lo tanto, para las aplicaciones del mundo real, un algoritmo hash adecuado es más apropiado.

Otra cosa a tener en cuenta aquí es el uso de useRef . useRef se utiliza para conservar los datos durante todo el ciclo de vida del componente envolvente. Dado que args es una matriz, que es un objeto en JavaScript, cada nueva representación del componente que usa el gancho hace que el puntero de referencia de args cambie. Pero args es parte de la lista de dependencias en nuestro primer useEffect . Entonces, el cambio useEffect args ejecute incluso cuando nada cambió. Para contrarrestar eso, hacemos una comparación profunda entre los argumentos antiguos y actuales usando args y solo permitimos que se ejecute la devolución de llamada useEffect si los args realmente cambiaron.

Ahora, podemos usar este nuevo useStaleRefresh de la siguiente manera. Observe el cambio en defaultValue aquí. Dado que es un enlace de propósito general, no confiamos en nuestro enlace para devolver la clave de data en el objeto de respuesta.

 export default function Component({ page }) { const [users, isLoading] = useStaleRefresh( apiFetch, [`https://reqres.in/api/users?page=${page}`], { data: [] } ); if (isLoading) { return <div>Loading</div>; } const usersDOM = users.data.map(user => ( <p key={user.id}> <img src={user.avatar} alt={user.first_name} style={{ height: 24, width: 24 }} /> {user.first_name} {user.last_name} </p> )); return <div>{usersDOM}</div>; }

Puede encontrar el código completo en este CodeSandbox.

No haga esperar a los usuarios: use el contenido de la caché de manera efectiva con obsoletos mientras se actualizan y React Hooks

El gancho useStaleRefresh que creamos en este artículo es una prueba de concepto que muestra lo que es posible con React Hooks. Intente jugar con el código y vea si puede encajarlo en su aplicación.

Alternativamente, también puede intentar aprovechar obsoleto mientras se actualiza a través de una biblioteca de código abierto popular y bien mantenida como swr o react-query. Ambas son bibliotecas poderosas y admiten una gran cantidad de características que ayudan con las solicitudes de API.

React Hooks es un cambio de juego. Nos permiten compartir la lógica de los componentes con elegancia. Esto no era posible antes porque el estado del componente, los métodos del ciclo de vida y la representación estaban empaquetados en una sola entidad: componentes de clase. Ahora, podemos tener diferentes módulos para todos ellos. Esto es excelente para la composición y para escribir un mejor código. Estoy usando componentes de función y ganchos para todo el nuevo código de React que escribo, y lo recomiendo encarecidamente a todos los desarrolladores de React.

Relacionado: Creación de aplicaciones React con Redux Toolkit y RTK Query