React Query offline
Este artículo es una traducción del post "Offline React Query" publicado por Dominik en su blog TkDodo.eu
Ya se ha mencionado en numerosas ocasiones: React Query es un gestor asíncrono de estado. Siempre que le des una Promesa, resuelta o rechazada, la librería estará contenta. No le importa de dónde venga esta Promesa.
Hay muchas formas de crear promesas, pero claramente el caso de uso más frecuente es solicitar data. Muy a menudo, esto requiere una conexión active de red. Pero algunas veces, especialmente en dispositivos móviles donde la conexión de red puede no ser fiable, necesitarás que tu app funcione sin ella.
Problemas en la v3
React Query está muy bien equipado para gestionar situaciones offline. Al tener una capa de cacheado, mientras el caché esté lleno, puedes seguir trabajando aunque no tengas conexión de red. Veamos tres casos donde la v3 no funcionaba como se esperaría. Usaremos el ejemplo básico de la documentación:
1. Sin data en el caché
En la v3, todo funciona bien mientras el caché tenga data. Un caso donde las cosas empiezan a fallar es el siguiente:
- Con buena conexión, navegas a la vista de lista.
- Pierdes la conexión y haces clic en un post.
Lo que ocurre es que tu solicitud estará en estado loading
hasta que recobres la conexión. Además, puedes ver una llamada de red fallida en las devtools del navegador. Esto es porque React Query siempre lanza la primera solicitud, y si falla, pausará los reintentos si no tienes conexión.
Además, las Devtools de React Query mostrarán que tu solicitud está fetching
, lo cual no es del todo cierto. La solicitud está realmente paused
, pero no había forma de representar ese concepto hasta la v4.
2. Sin reintentos
De manera similar, si en el escenario anterior hubieras desactivado los reintentos completamente, tu solicitud iría a un estado de error
inmediatamente, sin forma de pararlo.
¿Por qué necesito reintentos para que mi solicitud vaya a paused
si no tengo coenxión de red 🤷♂️?
3. Solicitudes que no necesitan red
Las solicitudes que no necesitan la red para funcionar (por ejemplo, hacer un procesado asíncrono costoso en un web worker) se pausarán hasta que tengas conexión si fallaran por cualquier otra razón. Además, esas solicitudes no se ejecutarán al enfocar la ventana porque esta funcionalidad está completamente deshabilitada si no tienes conexión.
En resumen, hay dos grandes problemas: En algunos casos, React Query asume que la conexión es necesaria cuando en realidad puede que eso no sea cierto (caso 3); y en otros casos, React Query lanza una solicitud aunque quizás no debería (casos 1 y 2).
El nuevo networkMode
En la v4 y posteriores se intenta abordar este problema de forma holística con un nuevo ajuste de “modo de red” o networkMode
. Con esto podemos diferenciar claramente entre solicitudes online y offline.
Es una opción para useQuery
y para useMutation
, lo que significa que se puede ajustar globalmente o caso por caso. Después de todo, puede que tengas algunas solicitudes que necesitan conexión, y otras que no.
Modo online
Este es el modo por defecto, ya que se espera que la mayoría de usuarios utilicen React Query con solicitudes de data. En definitiva, con este setting se asume que una solicitud solo puede ejecutarse si tiene una conexión de red activa.
Así que, ¿qué pasa si quieres ejecutar una solicitud que necesita conexión cuando no la tienes? La solicitud se pondrá en un nuevo estado paused
. Este estado es secundario al estado principal que puede ser loading
, success
o error
, ya que puedes perder la conexión en cualquier momento.
Esto quiere decir que puedes estar en estado success
y paused
, por ejemplo, si has obtenido data con éxito una vez, pero una re-solicitud en segundo plano se pausó.
O puedes estar en estado loading
y paused
si una solicitud se monta por primera vez.
Ya teníamos fetchStatus
Siempre hemos tenido la opción isFetching
que indicaba si una solicitud estaba en marcha. Similar al nuevo estado paused
, una consulta podía estar en success
y fetching
, o en error
y fetching
. Las recargas en segundo plano te dan muchos estados posibles (👋 máquinas de estado).
Como fetching
y paused
son mutuamente excluyentes, se han combinado en el nuevo fetchStatus
que es devuelto por useQuery
:
fetching
: La consulta se está ejecutando realmente - una solicitud está en marcha.paused
: La consulta no se está ejecutando - está pausada hasta que recuperes la conexión.idle
: La consulta no está en marcha.
Como regla general, el status
de la consulta te dará información sobre la data: success
significa que tienes data, loading
significa que aún no tienes data.
Por otro lado, el fetchStatus
te da información sobre la queryFn
: ¿Se está ejecutando o no? Las opciones isFetching
e isPaused
se derivan de este estado.
Revisemos cómo sería el anteiror caso 1 en la v4. Fíjate en el botón para cambiar el modo de red en las Devtools de RQ. Puedes hacer creer a React Query que no tiene red en lugar de desconectarte realmente.
Puedes ver claramente el estado en el que está la consulta (paused
) gracias a la nueva etiqueta morada. Además, la primera solicitud de red se llama cuando volvemos a conectar la red.
Modo always
En este modo React Query no se preocupa en absoluto por tu conexión de red. Las consultas se lanzan siempre, y nunca se pausarán. Esto es especialmente útil si usas React Query para otras cosas que no sean obtener data.
Modo offlineFirst
Este modo es muy similar a como Reat Query funcionaba en la v3. La primera solicitud se hace siempre, y si esa falla se pausarán los reintentos. Este modo es útil si tienes una capa de caché adicional sobre React Query, como el caché del navegador.
Veamos la API de repositorios de GitHub. Manda las siguientes cabeceras:
cache-control: public, max-age=60, s-maxage=60
…lo que siginifca que, durante los siguientes 60 segundos, si solicitas este contenido de nuevo, la respuesta vendrá del caché del navegador. ¡Lo mejor de esto es que funciona mientras estás offline! Los service workers (por ejemplo las PWA offline-first) funcionan de forma similar, intereptando la solicitud de red y devolviendo respuestas cacheadas si están disponilbes.
Y todo esto no funcionaría si React Query decidiera no lanzar la solicitud porque no tienes conexión, como hace el modo por defecto online
. Para interceptar una solicitud, esta tiene que suceder primero! Así que si tienes está capa adicional de caché, ajusta networkMode: 'offlineFirst'
.
Si la primera solicitud se lanza y encuentra el caché, todo bien: tu consulta irá a estado success
y obtendrás esa data. Y si el caché falla, seguramente tendrás un error de red, tras lo que React Query pausará los reintentos, podiendo tu consulta en estado paused
. Lo mejor de ambos mundos 🙌.
¿Qué significa todo esto para mí, concretamente?
Nada, a no ser que quieras. Puedes ignorar fetchStatus
y solo comprobar isLoading
: React Query se comportará como siempre (bueno, incluso el caso 2 anterior funcionará mejor porque no verás el error de red).
Aun así, si tener una app preparada para casos en lo que no haya red es una prioridad para ti, ahora tienes la opción de observar el fetchStatus
expuesto y actuar en consecuencia.
La serie completa
Este post es parte de la serie React-Query por Tkdodo que he traducido desde su blog. Mira todos los artículos:
- Consejos prácticos sobre React Query
- Transformación de data en React Query
- Optimización del renderizado en React Query
- Comprobar el estado en React Query
- Tests en React Query
- React Query y TypeScript
- Usar Websockets con React Query
- Claves eficaces en React Query
- Data inicial y de ejemplo en React Query
- React Query como un Gestor de Estado
- Gestión de Errores en React Query
- Domina las Mutaciones en React Query
- React Query offline
- Formularios en React Query
No hay sección de comentarios, pero me encantaría escuchar tu opinión: escríbeme en Twitter y cuéntame!