React_artboard
Paso a paso

Crea tu sistema de cache con React Context

Sin duda has notado muchas veces que tu carpeta node_modules ya pesa más que tú después de las fiestas decembrinas y te puedo apostar que ninguna de las dependencias que se instalan en tu proyecto las utilizas al 100%. Para evitar hacer tantas peticiones a un servidor desde un cliente creado con React, ya no es necesario instalar ni una más ya que aquí te escribo otra solución y así crear tu sistema de caché solamente con React Context.

Te daré un ejemplo que me pasa muy a menudo con los usuarios de mi aplicación. En mi interfaz existen check buttons los cuales piden los registros filtrados por algún tema en específico y al quitar el filtro, regresa el listado original. Cada interacción lanza una petición y podrías estar pensando: ¡solo son dos peticiones!. Sí, estoy de acuerdo, pero ¿qué pasa si ellos requieren ver la información nuevamente en un lapso de tiempo muy corto? Tendrán que presionar el botón una vez más, al grado que la acción se torna repetitiva N veces por cada usuario que existe. Hasta este punto, muchas veces las consultas llegan a ser la mismas en lapsos muy pequeños y dependiendo de la cantidad de información que manejes, estas peticiones pueden llegar a sobrecargar con gran cantidad de operaciones a las bases de datos y saturar ya sea la memoria, CPU, el número de conexiones, u otros recursos, ¿Cómo se puede evitar esto? con un sistema de caché.

Empecé a buscar artículos para poder implementarlo con React y encontré varios donde se utiliza Redux. Algunos desarrolladores hasta crearon dependencias para que nosotros solo las instalemos en nuestros proyectos y así solucionar el problema del caché. Llegué a experimentar con una de ellas en mi proyecto pero el problema que visualizaba era que se trataba de llegar a la solución de una manera algo compleja y enredosa, un ejemplo claro es esta dependencia, la instalación y su uso conllevan a hacer una serie de pasos extras para poder utilizarlo y además de que solo sirve para una sola petición, es decir, no guarda más datos de diversas acciones en la memoria. Lo que necesitaba era justo eso, tener la información de varias acciones seguidas para no repetir peticiones en un lapso de tiempo muy corto.

Así iba caminando por el mundo, triste y desamparado hasta que llegó la solución…

Lo que te voy a presentar es algo basado en una conferencia que se dio durante la F8 2017, donde se platicó la forma de construir experiencias offline para Instagram (si te interesa verlo te dejo el enlace). Un compañero y yo analizamos el concepto de esa presentación y se nos ocurrió la forma de aplicar el caché muy fácilmente. Primero lo desarrollamos en Redux por lo que eliminamos la otra dependencia sobrante, y cumplimos con nuestro cometido en la primer iteración. La idea prácticamente consiste en lo siguiente:

Diagrama función cache

Tenemos la interfaz que lanza una petición, pero no sin antes pasar por un “Middleware” que contiene una función que se encarga de averiguar si dicha petición ya fue lanzada previamente. Si fue así, el sistema regresa los datos guardados en el caché con los parámetros que el usuario pidió, si no, se realiza la petición al servidor y retorna los datos de igual manera.

Pero ¿y cómo se guardan los datos en el caché? Bueno, el “Middleware” también cuenta con la opción de guardar la petición junto con su respuesta en la memoria siempre que se requiera del servidor. Almacenamos cierto número de peticiones según sea el caso y al llegar a su límite establecido, se empieza a sacar las más antiguas para dar paso a las entrantes.

Diagrama guardado cache

Sencillo, ¿no?. Esto fue implementado con un reducer y un action en ese entonces y funcionó de maravilla, pero con la casi reciente actualización de React Context, ¡esta idea se convirtió en algo aún mejor! y ahora solo depende de una sola dependencia: React y nada más React. Asumiré que tú que estás leyendo este artículo ya has leído como funciona Context, así que sin más preámbulos te digo como construir el caché:

Crea un HOC el cual fungirá como el “Middleware” y se encargará de la lógica del funcionamiento y el guardado del caché.

En este caso codifiqué la url como hash, pero pueden ser los parámetros de tu petición, o lo que tú consideres una buena forma para identificar los datos solicitados.

El arreglo del caché vive dentro del state del HOC junto con un arreglo para almacenar los datos actuales que hay que regresar a la inferfaz, y también junto con un booleano que indica si la petición está siendo extraída del servidor.

La razón por la que no usé el localStorage es porque manejar un FIFO dentro de él implica que el navegador realice muchas operaciones internas y tú como desarrollador escribas un poco más de código de lo debido. Pero si lo que necesitas es más persistencia de los datos, adelante.

La función fetchApi() representa la forma de averiguar si la petición ya fue lanzada previamente o lanzar una nueva. Transforma la petición entrante en hash y busca en el caché si existe o no. Lo que pasa después es tal y como te lo expliqué anteriormente.

Si necesitas cambiar la función ‘fetch’ nativa de Javascript por otra biblioteca, por ejemplo Axios, es completamente lo mismo. Solo sigue el mismo algoritmo.

La función updateCache() juega el papel de guardar y sacar los objetos del FIFO del caché y retornar los datos del servidor. De igual manera es como lo expliqué anteriormente en el diagrama.

Este HOC puede ser llamado desde cualquier componente, solo que te recomiendo que sea desde un contenedor. Por ejemplo, tienes una interfaz con unos filtros de lado izquierdo de la pantalla y un listado con los datos que se piden en el lado derecho, el contenedor será el componente que englobe esas dos secciones en uno solo.

Así el provider actuará solo y directamente en los hijos de ese contenedor. Si necesitas guardar peticiones en otras secciones, simplemente importa el HOC en esos casos.

Y finalmente los componentes que lanzan las peticiones dentro del contenedor y los que pintan los datos recabados se tienen que conectar al consumer del HOC.

Ambos componentes obtienen solamente los datos de la petición solicitada, el booleano de carga y la función para pedir otros datos. Cada uno utiliza los props necesarios para cumplir su cometido.

¡Y listo! Solo observa tu pestaña de Red en el inspector de tu navegador y verás que al jugar con los botones no se lanzarán tantas peticiones y tendrás los datos en un instante. Te dejo este ejemplo corriendo en vivo 100% real, no fake aquí.

¿Qué opinas? Para mí es mucho más sencillo que las otras soluciones que se encuentran en la red. Si no es así, con gusto nos vamos a discutir tu idea con una taza de café.