Compound Component Pattern en React
Tags: react, javascript, webdev, programming
Published: 2022-01-10 02:58:45

Podemos definir un patrón como una técnica que nos permite llegar a la resolución de problemas de distinta índole, como por ejemplo, problemas de comportamiento, estructurales, creacionales, entre otros.

En este caso, el **patrón de composición de componentes** o **Compound Component Pattern** aplicado a React, nos permite manejar la información desde un componente padre y tener control total de todos los componentes hijos.

```javascript
<ProductCard  product={product} className="bg-dark">
   <ProductImage />
   <ProductTitle  />
   <ProductButtons />
</ProductCard>
```

En este caso tenemos el componente ProductCard, el cual recibe como **children** 3 componentes los cuales también reciben información desde el padre a través del **React Context**, a continuación los veremos a detalle

##ProductCard##

```javascript
import React, {
  createContext,
  ReactElement,
} from "react";
import styles from "../styles/styles.module.css";
import useProduct from "../hooks/useProduct";
import { Product, ProductContextProps } from "../interfaces";
import { ProductImage } from "./ProductImage";
import { ProductTitle } from "./ProductTitle";
import { ProductButtons } from "./ProductButtons";

export const ProductContext = createContext({} as ProductContextProps);
const { Provider } = ProductContext;


 interface Props {
  product: Product;
  children: ReactElement | Array<ReactElement>;
  className: string;
}

const ProductCard = ({ children, product, className }: Props) => {
  const { counter, increaseBy } = useProduct();

  return (
    <Provider
      value={{
        counter,
        increaseBy,
        product,
        className
      }}
    >
      <div className={styles.productCard}>
        {children}
      </div>
    </Provider>
  );
};

export default ProductCard;
```

##ProductImage##

```javascript
import { useContext } from "react";
import { ProductContext } from "./ProductCard";
import noImage from "../assets/no-image.jpg";
import styles from "../styles/styles.module.css";

export const ProductImage = ({ img = "" }) => {
    const { product } = useContext(ProductContext);
    let imgToShow: string;
  
    if(img) {
        imgToShow = img;
    } else if(product.img) {
        imgToShow = product.img
    } else {
        imgToShow = noImage
    }
  
    return (
      <img
        className={styles.productImg}
        src={imgToShow}
        alt="Coffee Mug"
      />
    );
  };
```

##ProductTitle##

```javascript
import { useContext } from "react";
import { ProductContext } from "./ProductCard";
import styles from "../styles/styles.module.css";

export const ProductTitle = ({ title }: { title?: string }) => {
  const { product } = useContext(ProductContext);

  return (
    <span className={styles.productDescription}>
      {title ? title : product.title}
    </span>
  );
};
```
##ProductButtons##

```javascript
import { useContext } from "react";
import { ProductContext } from "./ProductCard";
import styles from "../styles/styles.module.css";

export const ProductButtons = () => {
  const { increaseBy, counter } = useContext(ProductContext);

  return (
    <div className={styles.buttonsContainer}>
      <button className={styles.buttonMinus} onClick={() => increaseBy(-1)}>
        -
      </button>
      <div className={styles.countLabel}>{counter}</div>
      <button className={styles.buttonAdd} onClick={() => increaseBy(+1)}>
        +
      </button>
    </div>
  );
};
```

Como podemos ver, cada componente hijo recibe diferentes Props como **counter, increaseBy, product y className**, sin embargo a través del React Context, tenemos el control total desde nuestro componente padre. Esto nos abre una gran variedad de posibilidades para construir componentes reutilizables e incluso librerías personalizadas. Finalmente, aclarar que la meta principal de este patrón es lograr que nuestro componente sea lo más flexible posible.