React. useContext e inyección de dependencias
Cómo usar el hook useContext para inyectar dependencias en los componentes
English version: https://medium.com/@ger86/react-usecontext-and-dependency-injection-6807902e1695
Si estáis familiarizados con los principios SOLID para la creación de aplicaciones mantenibles y extensibles, sabréis que uno de ellos establece la regla de depender de abstracciones y no de clases concretas, también conocido como “Dependency Inversion Principle”. Uno de los patrones que permiten cumplir con este principio es el conocido como “Inyección de dependencias”, el cual nos ayudara a desacoplar los componentes que forman nuestra aplicación, escribir tests unitarios y modificar o añadir funcionalidad de forma mucho más sencilla.
Si bien algunos frameworks poseen ya elementos creados exclusivamente para esta tarea, en React es necesario tener algo más de imaginación si queremos adoptar este patrón. Por suerte, desde la versión 16 de React tenemos el componente Context a nuestra disposición para implementar este patrón de forma bastante sencilla. ¡Vamos a verlo!
Introducción
Como este tipo de cosas se ven mejor con ejemplos lo que haré será aplicar React Context para usar distintas librerías que sirvan para almacenar datos como localStorage, AWS, etc.
Además, emplearé como lenguaje TypeScript ya que llevo un tiempo familiarizándome con él de cara a emplearlo en proyectos más grandes (tanto su sistema de tipado como algunas de las otras herramientas que provee me han resultado muy interesantes).
La interfaz AsyncWebStorage
Definiremos la interfaz AsyncWebStorage
del siguiente modo:

Esta interfaz puede ser implementada por cualquier sistema de almacenamiento que se nos ocurra. Por ejemplo, el más sencillo puede ser un sistema en memoria por medio de un Map
:

Uso de React Context
Una vez creada nuestra primera implementación de la interfaz AsyncWebStorage
lo que haremos será emplear React Context para proveer por defecto este componente como sistema de almacenamiento para nuestra aplicación:

A continuación lo que haremos será crear un componente que empleará el sistema de almacenamiento presente en Context para leer datos de él. Esta dependencia la recibirá desde Context y gracias a la abstracción que proporciona la interfaz AsyncWebStorage
para él será transparente el sistema empleado por debajo para guardar y leer datos

Esta dependencia es recibida gracias al Provider
que proporciona AsyncWebStorageContext
y que envuelve a los componentes que necesiten trabajar con esa información:

Si quisiéramos cambiar el sistema de almacenamiento sería tan sencillo como crear otro componente que extendiese la interfaz AsyncWebStorage
e inyectarla por medio de AsyncWebStorageContext
pero sin necesidad de modificar en ningún momento el componente Foo
:


Lo cual cumple también con el principio SOLID “open-closed” pues el componente Foo
se encuentra cerrado a su modificación pero abierto a su extensión gracias a la intefaz provista por AsyncWebStorage
.
Conclusiones
Como habéis podido ver React.Context
no es sólo una alternativa a Redux como sistema de gestión del estado global de nuestra aplicación sino que también puede emplearse para aplicar patrones de diseño como el que acabamos de ver.
Si conocéis otros usos para la API Context de React podéis dejarlos en los comentarios, ¡me encantará leerlos!
¿Quieres recibir más artículos como este?
Si te ha gustado este artículo te animo a que te suscribas a la newsletter que envío cada domingo con publicaciones similares a esta y más contenido recomendado: 👇👇👇