¿Cuántas veces os ha pasado que por un rediseño habéis tenido que rehacer el componente, tanto la parte lógica como la UI? ¿Cuántas veces habéis intentado llevar un componente de un repositorio a otro y ha sido un verdadero dolor, o incluso imposible?

Para solucionar este tipo de problemas, aparecen los headless components.

Los headless components son componentes funcionales que tienen la lógica y comportamiento separados de la UI. Es decir, son componentes que no renderizan una UI pero que son creados para que otros componentes lo consuman.

Estos componentes, al tener lógica totalmente desacoplada, son fácilmente reutilizables.

Vamos a verlo con un ejemplo de un toggle, el más básico que os podréis encontrar, pero muy ilustrativo para entender en qué consisten los headless components.

La funcionalidad de un toggle es muy sencilla. Controla cuándo un elemento está activo y, cuando se hace click sobre él, lo activa o desactiva según sea su estado.

import { useState } from "react";

function useToggle(initialValue = false) {
  const [toggled, setToggle] = useState(initialValue);

  return { toggled, setToggle };
}

export default useToggle;

El código es muy sencillito. Tenemos un custom hook que controla si el componente está activo o no.

Ahora vamos a crear un componente que consuma nuestro headless component. Este va a ser un label y un checkbox.

import React from "react";
import useToggle from "./useToggle";

function Toggle({ name }) {
  let { toggled, setToggle } = useToggle();
  return (
    <div className="checkbox">
      <label htmlFor={name}>
        Toogle
        <input
          type="checkbox"
          id={name}
          name={name}
          checked={toggled}
          onChange={() => setToggle(!toggled)}
        />
      </label>
    </div>
  );
}

export default Toggle;

A nuestro componente Toggle no vamos a darle ningún estilo. Va a ser un checkbox con un label con aspecto nativo. Como vemos, lo único que hemos hecho ha sido utilizar dentro de nuestro componente headless useToggle(). Para esto, ponemos un evento onChange en el label y este evento lo único que va a hacer es cambiar el active, que por defecto está a false, y ponerlo a true.

Ya tenemos nuestro headless component más básico funcionando.

Ahora vamos a implementarlo en otra UI diferente, en un switch component.

import React from "react";
import useToggle from "./useToggle";

function Toggle({ name }) {
  let { toggled, setToggle } = useToggle();
  return (
    <div className="checkbox">
      <label htmlFor={name}>
        Toogle
        <input
          type="checkbox"
          id={name}
          name={name}
          checked={toggled}
          onChange={() => setToggle(!toggled)}
        />
      </label>
    </div>
  );
}

export default Toggle;

En este caso, nuestro switch va a tener una estructura muy similar al toogle. Tendrá un label con un checkbox dentro y en el after del label vamos pintar el puntero del switch, que irá moviéndose cada vez que se haga click. Exactamente igual que en el caso anterior, mediante el evento onChange utilizamos setToggle para cambiar el estado cada vez que se haga click. Esta vez hemos aplicado estilos y tenemos la siguiente UI:

Y ahora vamos a aplicarlo a otro componente que solemos utilizar muchísimo: un dropdown.

import React from "react";

import useToggle from "./useToggle";

function Dropdown({ name }) {
  let { toggled, setToggle } = useToggle();

  return (
    <div className={toggled ? "dropdown active" : "dropdown"}>
      <input
        className="switch-checkbox"
        type="checkbox"
        id={name}
        name={name}
        checked={toggled}
        onChange={e => setToggle(!toggled)}
      />
      <label htmlFor={name} className="dropdown-item">
        <span>Cónocenos</span>
        <span className="dropdown-arrow">
          <svg
            version="1.1"
            xmlns="http://www.w3.org/2000/svg"
            width="24"
            height="24"
            viewBox="0 0 24 24"
          >
            <path d="M7.406 8.578l4.594 4.594 4.594-4.594 1.406 1.406-6 6-6-6z" />
          </svg>
        </span>
      </label>
      {toggled && (
        <ul className="dropdown-content">
          <li>
            <a href="#visitanos">Visítanos</a>
          </li>
          <li>
            <a href="#cultura">Cultura</a>
          </li>
          <li>
            <a href="#oficinas">Oficinas</a>
          </li>
        </ul>
      )}
    </div>
  );
}

export default Dropdown;}

En este caso el código es muy parecido, excepto que renderiza el menú del dropdown una vez que está activo. Como véis, la implementación del headless component es exactamente igual: usamos el onChange con el setToggle.

Aplicando css tendríamos la siguiente UI con el dropdown desplegado:

Como habéis podido ver, hemos aplicado un simple headless component a tres UI diferentes de forma muy sencilla.

Esto no significa que haya que volverse loco usando headless components. ¿En qué casos deberíamos utilizarlos? Básicamente para componentes que tengan lógica y que vayamos a reutilizar, por ejemplo para librerías de componentes y guías de estilos.

En conclusión, crear componentes con la lógica totalmente desacoplada nos permite reutilizar nuestros componentes y la implementación es sencilla, tal y como hemos visto en los ejemplos. Son totalmente testeables, flexibles, reutilizables en distintos proyectos y fáciles de mantener.

¿A qué estás esperando para empezar a utilizarlos?

Cuéntanos qué te parece.

Los comentarios serán moderados. Serán visibles si aportan un argumento constructivo. Si no estás de acuerdo con algún punto, por favor, muestra tus opiniones de manera educada.

Suscríbete

Estamos comprometidos.

Tecnología, personas e impacto positivo.