Usando High Order Component (HOC) para generalizar componentes

Daniel Rivera
Nursoft
Published in
5 min readFeb 15, 2019

Conforme se van desarrollando en Nursoft nuevos proyectos y/o aplicaciones, es común comenzar a identificar patrones de diseño o elementos que se van repitiendo a través de cada proyecto (login, administración de usuarios, etc).

Uno de los desafíos de trabajar en Nursoft es poder crear proyectos dealta complejidad usando metodologías ágiles, esto nos permitan un desarrollo veloz y de alta calidad. Uno de los factores claves para lograr este objetivo es la reutilización de componentes en el software, en el área de manejo de datos, diseño e interacción del usuario .

Uno de los frameworks implementados para el desarrollo front-end de aplicaciones Web es el conocido React, dado que este posee herramientas enfocadas para trabajar bajo un principio de composición, con el fin de reutilizar lógica y código, tanto dentro como entre proyectos.

Dentro de estas herramientas, una de las mas poderosas pero complejas de implementar correctamente por su nivel de abstracción son los componentes de alto nivel, o Higher-Order Component (HOC). Estos nos permiten reutilizar lógica entre componentes, y/o abstraer estos comportamientos de cada componente en sí, generando así código más limpio, estructurado, facilitando un desarrollo y testing más eficaz y ágil.

En esta publicación, vamos a desarrollar un pequeño proyecto para observar los alcances de los componentes de alto nivel, y determinar los casos en qué estos pueden ser realmente útiles para agilizar el desarrollo front-end de una aplicación web.

¿Qué es un componente de alto nivel?

En React, los componentes de alto nivel no son una herramienta o una librería adicional, sino que es patrón de diseño que emerge naturalmente de los principios del framework, el cual es la predisposición a la composición sobre la herencia.

Si bien es sabido que su implementación puede resultar un poco engorrosa, un componente de alto nivel es básicamente una función, que toma como parámetro un componente y retorna también un componente.

Ejemplo de una función de componente de alto nivel

El componente retornado es llamado como un componente envuelto o <WrappedComponent/>, de esta forma uno puede inyectar funcionalidades o ciclos de estado a otro componente, pero este en si es independiente del componente que lo envuelve.

En la demo se implementan 2 casos de usos generales que se observan en diversos proyectos ¿es posible generalizar la funcionalidad de Infinite Scrolling o de Drag and Drop en un proyecto?. El enfoque es que independiente de la naturaleza del componente interior, uno puede inyectar fácilmente funcionalidades y atributos tanto un componente individual como a un set de componentes.

Ejemplo: Infinite Scrolling

El primer ejemplo busca capturar el comportamiento del InfiniteScroll, tanto en una lista de elementos, como en una galería de imágenes. En ambos casos, se inyecta el componente de alto nivel en la siguiente función:

Componente CustomBox.js

Componente CustomGallery.js

Una de las ventajas más importantes respecto al diseño y estructura de una aplicación es lo simple de los códigos de los componentes, dado que los 3 componentes personalizados (CustomList, CustomGallery, CustomBox) no poseen más de 20 líneas de código, y solo se preocupan de las propiedades inherentes a ellos, y no el contexto de implementación, el cual puede variar (usando otros componentes de alto nivel).

La función que permite crear el componente de alto nivel es el siguiente:

La lógica que busca capturar el comportamiento se encuentra en el archivo withScrolling.js. En la función compontentDidMount(), parte del ciclo de los componentes en React, se agrega el evento scroll al componente envuelto, implementando el comando refs. De esta forma, cada vez que el usuario realiza scrolling en el componente, la función onScroll() será llamada.

La función onScroll() verifica un set de condicionales que en resumen dice “Si la barra de scroll se encuentra en el 70% (variable fija, esta puede modificarse) de la altura total recorrida, ejecutar la función endScrolling()", siendo esta función entregada como parámetro al momento de envolver el componente original.

Esto nos permite tener control de qué función ejecutar al momento de que el scroll del componente se encuentre en un determinado componente. Esta función puede generar un nuevo request, actualizar el estado interno del componente, etc. Finalmente, la función compontentWillUnmount(), también parte del ciclo de componentes de React, remueve el evento scroll al momento al momento de que el componente sea desmantelado de la página.

Una de las razones por la cual resulta complejo implementar componentes de alto nivel, independiente de la simpleza de su código, es que primero es necesario identificar la naturaleza del comportamiento de los componentes que se busca generalizar y la similitud entre los componentes y/o sistema de componentes. En este caso, CustomList y CustomGallery, son una repetición de elementos alineados bajo cierta estructura, y donde el tamaño de este conjunto de elementos sobrepasa el tamaño visible, de modo que es necesario realizar scrolling para navegar por los distintos elementos de ambos conjuntos, por lo que ambos componentes interactúan con el evento onScroll.

Ejemplo: Drag & Drop

Un evento un poco más complejo es la funcionalidad Drag & Drop , dado que para completar el ciclo de este (arrastrar un elemento, soltarlo en un sector válido y ejecutar una actualización) requiere de no solo un evento, sino de 8 eventos, en este caso, la generalización se vuelve un poco más compleja.

En el archivo withDragDrop.js , se encuentra la implementación del componente de alto nivel . Cada evento tiene un comportamiento default el cual no realiza nada (los elementos drag no necesariamente reaccionan ante un evento drop y vice versa).

En la demostración, el elemento de la izquierda , solo reacciona ante el evento onDragStart, es posible implementar handler para el resto de los eventos , pero es posible dejarlos con funciones que no retornan nada.

Los 9 elementos de la derecha en cambio, actúan como componentes que pueden reaccionar ante eventos onDrop, pero también pueden registrar sus propios eventos onDragStart.

Es importante destacar que para ambos casos fue usada la misma función withDragDrop, demostrando así la flexibilidad de los componentes de alto nivel. Cada una de los eventos pertenecientes al ciclo Drag & Drop pueden ser customizados.

Son muchos los casos en que la implementación de componentes de alto nivel pueden ayudarnos a generalizar lógicas y comportamientos de un número de componentes en un proyecto, abstraer la capa de funcionalidad de estos, y obtener así un código más estructurado y tiempo de desarrollo más óptimo.

Otros casos de usos de componentes de alto nivel pueden ser:

  • Render condicional más complejos para componentes, en donde la condición para visualizar un componente depende de diversos casos, como una respuesta de un webSocket, un estado en Redux, etc.
  • Visualizar un spinner, o un loader, sobre un componente específico, para un caso personalizado.
  • Responder a un Websocket específico, y realizar una función personalizada , para cada componente registrado con un componente de alto nivel

--

--