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 las bibliotecas populares de administración de estados.

Cuando un usuario carga un sitio web o una aplicación por primera vez, suele haber una gran cantidad de trabajo para construir el estado inicial de la aplicación que se usa para renderizar la IU. Por ejemplo, a veces la app necesita autenticar al usuario del lado 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 para 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, con una estrategia de stale-while-revalidate.

Sin embargo, cuando se usa IndexedDB, hay muchos aspectos importantes que se deben tener en cuenta y que pueden no ser evidentes de inmediato para los desarrolladores que no conocen las APIs. En este documento, se responden preguntas frecuentes 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 a 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 cuando se escribe en IndexedDB pueden ocurrir por 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 esté en un dispositivo que casi no tiene espacio en el disco y el navegador te impida almacenar cualquier cosa.

Por este motivo, es de suma importancia que siempre implementes un manejo de errores adecuado 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), de modo que la IU no se interrumpa cuando se ejecute en el modo de navegación privada o cuando no haya 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 bases de datos del cliente son accesibles para las extensiones del navegador y las herramientas para desarrolladores, y el usuario puede borrarlas.

Si bien es 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 controlar 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 se hayan escrito con una versión anterior de tu código, posiblemente una versión con errores.

IndexedDB tiene compatibilidad integrada con las versiones de esquema y la actualización con su método IDBOpenDBRequest.onupgradeneeded(). Sin embargo, debes escribir el 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 en este caso, 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 haga pensar que no debes preocuparte por el rendimiento cuando la uses. 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 lecturas y escrituras en IndexedDB no deben ser mayores que las requeridas para los datos a los que se accede.

Si bien IndexedDB permite almacenar objetos grandes y anidados como un solo registro (y, hay que admitirlo, esto es bastante conveniente desde la perspectiva de un desarrollador), se debe evitar esta práctica. Esto se debe a que, cuando IndexedDB almacena un objeto, primero debe crear una clonación estructurada de ese objeto, y el proceso de clonación estructurada se produce en el subproceso principal. Cuanto más grande sea el objeto, más largo 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 de administración de estado populares (como Redux) funcionan administrando todo el árbol de estado como un solo objeto de JavaScript.

Si bien administrar el estado de esta manera tiene muchos beneficios y almacenar todo el árbol de estado como un solo registro en IndexedDB puede ser tentador y conveniente, hacerlo después de cada cambio (incluso si se limita o se rechaza) generará 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 estado en un solo registro, debes dividirlo en registros individuales y solo actualizar los registros que realmente cambian.

Como ocurre con la mayoría de las prácticas recomendadas, esta no es una regla absoluta. En los casos en que no sea posible dividir un objeto de estado y solo escribir el conjunto de cambios mínimo, dividir los datos en subárboles y solo escribir esos datos sigue siendo preferible a escribir siempre todo el árbol de estado. Las pequeñas mejoras son mejores que ninguna mejora.

Por último, siempre debes medir el impacto en el rendimiento del código que escribes. 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 que comprendas qué estás optimizando.

Conclusiones

Los desarrolladores pueden usar mecanismos de almacenamiento del cliente, 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 correctamente puede mejorar drásticamente la experiencia del usuario, usarla de forma incorrecta o no controlar los casos de error puede generar apps dañadas y usuarios insatisfechos.

Dado que el almacenamiento del cliente involucra muchos factores fuera de tu control, es fundamental que tu código esté bien probado y controle correctamente los errores, incluso aquellos que, en un principio, parezcan poco probables.