Prácticas recomendadas para conservar el estado de la aplicación con IndexedDB

Conoce las prácticas recomendadas para sincronizar el estado de la aplicación entre IndexedDB y bibliotecas populares de administración de estado.

Cuando un usuario carga por primera vez un sitio web o una aplicación, suele haber una gran cantidad de trabajo involucrado en la construcción del estado inicial de la aplicación que se utiliza para renderizar la IU. Por ejemplo, a veces, la app necesita autenticar al usuario del cliente y, luego, realizar varias solicitudes a la API antes de tener todos los datos que necesita mostrar en la página.

Almacenar el estado de la aplicación en IndexedDB puede ser una excelente manera de acelerar el tiempo de carga de las visitas repetidas. Luego, la app puede sincronizarse con cualquier servicio de API en segundo plano y actualizar la IU con datos nuevos de forma diferida, empleando una estrategia de revalidación mientras está inactiva.

Sin embargo, cuando se usa IndexedDB, hay muchos aspectos importantes a tener en cuenta que pueden no ser evidentes de inmediato para los desarrolladores que son nuevos en las APIs. En este documento, se responden preguntas comunes y se analizan algunos de los aspectos más importantes que se deben tener en cuenta cuando se conserva el estado de la aplicación en IndexedDB.

Mantén tu app predecible

Muchas de las complejidades de IndexedDB se deben al hecho de que hay muchos factores sobre los que tú (el desarrollador) no tienes control. En esta sección, se exploran muchos de los problemas que debes tener en cuenta cuando trabajas con IndexedDB.

Es posible que las operaciones de escritura en el almacenamiento fallen.

Los errores de escritura en IndexedDB pueden deberse a varios motivos y, en algunos casos, estos motivos están fuera de tu control como desarrollador. Por ejemplo, algunos navegadores no permiten escribir en IndexedDB cuando se usa el modo de navegación privada. También existe la posibilidad de que un usuario se encuentre en un dispositivo que casi no tiene espacio en el disco, y el navegador no te permitirá almacenar nada.

Debido a esto, es muy importante que siempre implementes el manejo adecuado de errores en tu código de IndexedDB. Esto también significa que, por lo general, es una buena idea mantener el estado de la aplicación en la memoria (además de almacenarlo) para que la IU no falle cuando se ejecuta en modo de navegación privada o cuando no hay espacio de almacenamiento disponible (incluso si algunas de las otras funciones de la app que requieren almacenamiento no funcionan).

Es posible que el usuario haya modificado o borrado los datos almacenados.

A diferencia de las bases de datos del servidor, en las que puedes restringir el acceso no autorizado, las extensiones del navegador y las herramientas para desarrolladores pueden acceder a las bases de datos del cliente, y el usuario puede borrarlas.

Si bien puede ser poco común que los usuarios modifiquen sus datos almacenados de forma local, es bastante común que los borren. Es importante que tu aplicación pueda manejar ambos casos sin errores.

Es posible que los datos almacenados estén desactualizados

Al igual que en la sección anterior, incluso si el usuario no modificó los datos, también es posible que los datos que tiene en el almacenamiento los haya escrito una versión anterior de tu código, posiblemente una versión con errores.

IndexedDB tiene compatibilidad integrada con versiones de esquema y actualizaciones mediante el método IDBOpenDBRequest.onupgradeneeded(). Sin embargo, debes escribir tu código de actualización de manera que pueda controlar al usuario que proviene de una versión anterior (incluida una versión con un error).

Las pruebas de unidades pueden ser muy útiles aquí, ya que a menudo no es factible probar de forma manual todas las rutas de actualización y los casos posibles.

Mantén el rendimiento de tu app

Una de las funciones clave de IndexedDB es su API asíncrona, pero no dejes que eso te engañe y pienses que no necesitas preocuparte por el rendimiento cuando la usas. Existen varios casos en los que el uso inadecuado puede bloquear el subproceso principal, lo que puede provocar que no responda.

Como regla general, las operaciones de lectura y escritura en IndexedDB no deben ser más grandes de lo necesario para los datos a los que se accede.

Si bien IndexedDB permite almacenar objetos grandes anidados como un solo registro (y hacerlo es bastante conveniente desde la perspectiva de un desarrollador), se debe evitar esta práctica. El motivo es que, cuando IndexedDB almacena un objeto, primero debe crear un clon estructurado de ese objeto, y el proceso de clonación estructurado se produce en el subproceso principal. Cuanto más grande sea el objeto, mayor será el tiempo de bloqueo.

Esto presenta algunos desafíos cuando se planifica cómo conservar el estado de la aplicación en IndexedDB, ya que la mayoría de las bibliotecas populares de administración de estados (como Redux) funcionan administrando todo el árbol de estados como un solo objeto de JavaScript.

Si bien administrar el estado de esta manera tiene muchos beneficios y almacenar el árbol de estado completo como un solo registro en IndexedDB puede ser tentador y conveniente, hacerlo después de cada cambio (incluso si se reduce la velocidad o se anula el rebote) provocará un bloqueo innecesario del subproceso principal, aumentará la probabilidad de errores de escritura y, en algunos casos, incluso hará que la pestaña del navegador falle o deje de responder.

En lugar de almacenar todo el árbol de estados en un solo registro, debes dividirlo en registros individuales y solo actualizar los registros que realmente cambian.

Al igual que con la mayoría de las prácticas recomendadas, esta no es una regla de todo o nada. En los casos en los que no es posible dividir un objeto de estado y solo escribir el conjunto de cambios mínimo, es preferible dividir los datos en subárboles y solo escribirlos en lugar de escribir siempre todo el árbol de estado. Las pequeñas mejoras son mejores que no hacer ninguna.

Por último, siempre debes medir el impacto en el rendimiento del código que escribas. Si bien es cierto que las escrituras pequeñas en IndexedDB tendrán un mejor rendimiento que las grandes, esto solo importa si las escrituras en IndexedDB que realiza tu aplicación realmente generan tareas largas que bloquean el subproceso principal y degradan la experiencia del usuario. Es importante realizar mediciones para comprender para qué optimizas.

Conclusiones

Los desarrolladores pueden usar mecanismos de almacenamiento de clientes, como IndexedDB, para mejorar la experiencia del usuario de su aplicación, no solo conservando el estado en todas las sesiones, sino también disminuyendo el tiempo que se tarda en cargar el estado inicial en las visitas repetidas.

Si bien usar IndexedDB de forma correcta puede mejorar de forma significativa la experiencia del usuario, usarlo de forma incorrecta o no controlar los casos de error puede provocar que las apps fallen y que los usuarios no estén conformes.

Dado que el almacenamiento del cliente involucra muchos factores fuera de tu control, es fundamental que tu código se haya probado correctamente y maneje de forma adecuada los errores, incluso aquellos que en un principio parezca poco probable que ocurran.