De las funciones de orden superior a los Higher Order Components (HOC)

Ana Martínez Aguilar
6 min readAug 15, 2022

--

En React, un Higher Order Component (HOC) es una técnica que se emplea para reutilizar la lógica de los componentes. Como dice la documentación oficial, los HOCs no forman parte del API de React, si no que son un patrón muy útil para programar con este framework dada su arquitectura.

Además de ser útil para programar con React (si habitualmente programas en React, seguramente te has topado con un HOC aunque no lo hayas reconocido como tal), este patrón es muy interesante porque nos enseña a escribir un código más legible y menos propenso a bugs.

Para entender qué es y cómo funciona un Componente de Orden Superior, no hay más que entender otro concepto: las funciones de orden superior.

Funciones de orden superior

Una función de orden superior (higher-order function, HOF) es una función que tiene otra función como argumento o que devuelve una función, o ambas cosas. Estas funciones son muy frecuentes y, aunque no conozcas el término, es probable que las hayas utilizado.

En resumen, una función de orden superior cumple al menos una de estas condiciones:
— Recibe una función como argumento
— Devuelve una función

Callbacks

Veamos el primer caso: cuando una función recibe otra función como argumento. La función que pasamos como argumento se denomina callback, porque es llamada (called back, en inglés) y ejecutada por la función de orden superior. La sintaxis es la siguiente:

const fn = callback => callback()

En el siguiente ejemplo, greet es una higher-order function y sayHi es la función que utilizamos como callback.

const sayHi = name => { console.log(`Hi ${name}`) } const greet = (action, name) => { action(name) } greet(sayHi, ‘Elizabeth Bennet’) // Hi Elizabeth Bennet

Otro ejemplo típico de callback es como función escuchadora de un evento.

const showAlert = () => { alert(‘Alerta’) } button.addEventListener(‘click’, showAlert)

Funciones que devuelven funciones

Pasemos al segundo caso: una función que devuelve otra función.

const fn = () => () => ‘something’
function sayGoodbye() { return function(name) { console.log(`Goodbye ${name}`) } } sayGoodbye()(‘Mr. Darcy’) // Goodbye Mr. Darcy

Seguro que en tu código puedes encontrar muchos ejemplos de higher-order functions. Los tan socorridos métodos de array también utilizan este tipo de funciones: forEach, filter, map, reduce

Las funciones de orden superior son frecuentes y tienen una sintaxis sencilla, ya que cualquier función en JS puede tomar como argumento una función y devolver otra función. El uso de estos patrones es lo que se conoce como programación funcional y nos ayuda a escribir un código más legible y menos propenso a bugs (todo depende del cómo y el cuándo). Pueden evitar la repetición de código, disminuir las dependencias, elevar el nivel de abstracción…

En general, las funciones nos permiten esconder detalles y mostrar a través de un vocabulario “humano” el problema esencial que resuelven. Como dice Marijn Haverbeke en su famoso libro Eloquent JavaScript: “Las funciones de orden superior nos permiten abstraer sobre acciones, no solo sobre valores”

Composición de funciones

Como ya hemos dicho, una función de orden superior puede combinar tanto los callbacks como el devolver otra función. A través de la creación de nuevas funciones que combinan la funcionalidad de otras funciones previamente definidas podemos implementar patrones de desarrollo muy interesantes. Es lo que se conoce como Function Composition.

A grandes rasgos, consiste en crear funciones simples que se encarguen solamente de una pequeña parte de la lógica y, por otro lado, componer funciones más complejas que utilicen esas funciones pequeñas.

En este primer ejemplo, la función concat devuelve una función que recibe tres parámetros y retorna la concatenación o la suma de ellos. No es una función muy útil y en este ejemplo todavía no estamos utilizando ningún callback, pero nos sirve para comenzar a explicar la composición.

function concat() { return function (a, b, c) { return a + b + c } }

Si queremos saber qué resulta de unir ‘hi’, 12 y 3, podemos hacerlo de la siguiente manera:

const concatAux = concat() concatAux(‘hi’, 12, 3) // ‘hi123’

¿Qué está pasando aquí? Como vemos en el ejemplo, la función concat en ningún momento está ejecutando la otra función, solo la está devolviendo. Por lo tanto, es necesario ejecutarla, pero primero tenemos que ejecutar la propia función concat. Es de lo que se encarga concatAux.

const concatAux1 = concat() console.log(concatAux1) /* ƒ (a, b, c) { return a + b + c } */ concatAux(‘hi’, 12, 3) // ‘hi123’

Para que el ejemplo anterior quede aún más claro, vamos a prescindir de la variable concatAux y utilizaremos su valor mismo: la ejecución concat().

Ahora vamos a añadir un callback a esta lógica. Así, vamos a crear una función que devuelve otra función y que además recibe un callback. Con este ejemplo, en el que sumamos, multiplicamos y mostramos en la consola, podrás entender mejor la utilidad de este patrón de desarrollo.

function add(x, y) { return x + y } function multiply(x, y) { return x * y } function log(callback) { return function(x, y) { const result = callback(x, y) console.log(result) return result } } const addAndLog = log(add) addAndLog(2, 3) // 5 const multiplyAndLog = log(multiply) multiplyAndLog(2, 3) // 6

De nuevo, nota que la variable addAndLog ejecuta la función log, pero esto no es suficiente. Por lo tanto, es necesario ejecutar también esa función. Y así lo hacemos cuando llamamos a addAndLog(2, 3).

Componentes de orden superior (HOC)

Ya llegamos a la última parte. Una vez entendidas las higher-order functions, es sencillo comprender los componentes de orden superior (higher-order components, HOC). Mientras una función compuesta tiene una función como argumento y devuelve otra función, un componente de orden superior es una función que tiene un componente como argumento y devuelve otro componente.

const hoc = Component => ( props => ( <Component /> ) );

La misión de un higher component es transformar. El componente que se toma como argumento, es transformado (normalmente a través de las props) y devuelto como un nuevo componente. Para entenderlo mejor: mientras que un componente “normal” tranforma props en una interfaz de usuario, un higher-order component transforma un componente en otro componente.

Veamos un ejemplo práctico. En un proyecto, es muy habitual que algunos textos, imágenes y botones tengan tooltips.

Para ello, podemos crear un componente Tooltip y utilizarlo siempre que lo necesitemos. Sin embargo, como se trata de un uso frecuente, podemos optimizar nuestro código de manera que podamos colocar el tooltip simplemente a través de una prop en el componente que necesita mostrar el tooltip.

En primer lugar, creamos el HOC withTooltip.

En segundo lugar, los componentes que sean susceptibles de mostrar un tooltip (como Button o Text…), los exportamos con el HOC. En este caso, trabajamos con el componente Button.

Por último, cuando queramos que un botón muestre un tooltip, solo tenemos que llamar al componente Button y pasarle la prop correspondiente.

<Button tooltipText=”I am tooltip”> Click me </Button>

Como ves, la técnica es la misma que para escribir una función de orden superior. No hay que olvidar que los componentes de React no dejan de ser funciones y objetos, por lo que podemos aprovechar las técnicas de JavaScript para jugar con ellos.

--

--