Patrón de diseño Higher-Order Component (HOC) en React y JavaScript

Introducción

El patrón de diseño Higher-Order Component (HOC) es un patrón avanzado en React que se utiliza para reutilizar la lógica de los componentes. Un HOC es una función que toma un componente y devuelve otro componente con funcionalidades adicionales o modificadas. En este artículo, exploraremos el patrón de diseño HOC con ejemplos en React y JavaScript.

HOC en detalle

La idea detrás de este patrón es aprovechar la composición en lugar de la herencia para extender la funcionalidad de los componentes de React. Entonces con la ayuda de la composición podemos tomar un componente, añadirle funcionalides y luego devolver el componente.

Ejemplo básico de HOC en JavaScript

Antes de adentrarnos en React, veamos un ejemplo básico de HOC en JavaScript:

function withLogging(fn) {
  return function (...args) {
    console.log(`Llamando a la función: ${fn.name}`, args);
    const result = fn(...args);
    console.log(`Resultado: ${result}`);
    return result;
  };
}

function suma(a, b) {
  return a + b;
}

const sumaConLogging = withLogging(suma);
const resultado = sumaConLogging(3, 4); // Llamando a la función: suma [3, 4]
                                        // Resultado: 7

En este ejemplo, withLogging es un HOC que toma una función fn y devuelve una nueva función que registra el nombre de la función, sus argumentos y su resultado antes de llamarla.

Ejemplo de HOC en React

Ahora, veamos un ejemplo de HOC en React. Supongamos que tenemos dos componentes funcionales: ComponenteA y ComponenteB. Queremos agregar la funcionalidad de contar el número de veces que se hace clic en un botón en ambos componentes. En lugar de agregar la lógica de conteo en cada componente individualmente, podemos crear un HOC que envuelva ambos componentes y les proporcione la funcionalidad de conteo.

Aquí está el código:

import React, { useState } from 'react';

// HOC que agrega la funcionalidad de conteo
const withCounter = (WrappedComponent) => {
  const WithCounter = () => {
    const [count, setCount] = useState(0);

    const handleClick = () => {
      setCount(count + 1);
    };

    return <WrappedComponent handleClick={handleClick} count={count} />;
  };

  return WithCounter;
};

// ComponenteA sin la funcionalidad de conteo
const ComponenteA = ({ handleClick }) => {
  return <button onClick={handleClick}>Componente A</button>;
};

// ComponenteB sin la funcionalidad de conteo
const ComponenteB = ({ handleClick }) => {
  return <button onClick={handleClick}>Componente B</button>;
};

// Envolver ComponenteA y ComponenteB con el HOC
const ComponenteAConConteo = withCounter(ComponenteA);
const ComponenteBConConteo = withCounter(ComponenteB);

// Renderizar ComponenteA y ComponenteB con la funcionalidad de conteo agregada
const App = () => {
  return (
    <>
      <ComponenteAConConteo />
      <ComponenteBConConteo />
    </>
  );
};

export default App;

En este ejemplo, creamos un HOC llamado withCounter que acepta un componente como argumento y devuelve un nuevo componente llamado WithCounter. WithCounter tiene su propio estado para contar el número de clics y una función handleClick que actualiza el estado cada vez que se hace clic en el botón.

Luego, envolvemos los componentes ComponenteA y ComponenteB con el HOC withCounter para agregarles la funcionalidad de conteo. Finalmente, renderizamos los componentes envueltos ComponenteAConConteo y ComponenteBConConteo en nuestra aplicación principal.

De esta manera, logramos reutilizar la funcionalidad de conteo en múltiples componentes y mantener nuestro código más limpio y modular.