imagen

React: Composición de componentes

A veces tenemos componentes cuyo contenido puede variar de forma dinámica, e incluso la lógica del componente no depende de su contenido, por ejemplo un componente que actúa como contenedor al que llamaremos Card cuya principal función es renderizar texto que recibe por prop, pero después de ese texto debe renderizar uno o mas botones con acciones especificas.

La forma tradicional seria poner la lógica de que botones queremos mostrar en el componente contenedor y agregar complejidad innecesaria al componente, dado que su principal responsabilidad es solo mostrar contenido.

function Card({children}) {
  return (
    <div>
      <p>
        Lorem Ipsum is simply dummy text of the printing and typesetting 
        industry. Lorem Ipsum has been the industry s standard dummy
      </p>
      {unreadMessages.length > 0 && <ButtonUnread onSearch={setState}/>}
      {is_logged && <ButtonAddComment/>}
    </div>
  );
}

Acá es donde entra en escena la composición de componentes, que nos permite hacer uso de la propiedad children que react recibe en todos sus componentes. Esto permite que otros componentes les pasen hijos arbitrarios a nuestro componentes y nuestro componentes se limitara a renderizarlos donde se le indique sin involucrarse en la lógica de esos hijos.

De esta forma tenemos el primer punto a favor, limitar la responsabilidad de nuestro componente Card

function Card({children}) {
  return (
    <div>
      <p>
        Lorem Ipsum is simply dummy text of the printing and typesetting 
        industry. Lorem Ipsum has been the industry s standard dummy
      </p>
      {children}    
    </div>
  );
}

Ahora vamos al componente Padre, este utilizara el componente Card y se encargara de la lógica de los botones.

De esta forma tenemos el segundo punto a favor y es no tener que pasar los estados y propiedades que necesitan los botones al componente Card.

const App = () => {
  const [state, setState] = React.useState();
  return (
    <>
      <Card>
        // Podemos pasar 2 hijos
        {unreadMessages.length > 0 && <ButtonUnread onSearch={setState}/>}
        {is_logged && <ButtonAddComment/>}
      </Card>
      <Card>
        // o Podemos pasar N hijos
        {tareas.map((tarea) => (
          <Button text={tarea} />
        ))}
      </TodoList>
    </>
  );
};

Hay mas beneficios de usar composición de componentes, puedes profundizar detalles en la documentación oficial.

Pasarle props a los children
function Card({children, loading}) {
  const clonedElement = cloneElement(children, {loading})
  return (
    <div>
      <p>
        Lorem Ipsum is simply dummy text of the printing and typesetting 
        industry. Lorem Ipsum has been the industry s standard dummy
      </p>
      {clonedElement}    
    </div>
  );
}

Esto nos permite pasarle la propiedad loading a todos los childrens. (ver doc)