Composition Pattern: Exemplo prático no React
Post

Composition Pattern: Exemplo prático no React

Introdução

O padrão de composição (Composition Pattern) é um conceito comum na programação orientada a objetos, onde objetos são compostos de outros objetos para obter funcionalidades mais complexas. No contexto do desenvolvimento frontend com React, o padrão de composição é frequentemente utilizado para criar componentes reutilizáveis e modularizados.

Exemplo Simples

Vamos criar um exemplo simples para demonstrar o padrão de composição em React:

Suponha que queremos criar um componente Card que pode exibir diferentes tipos de conteúdo, como título, texto e uma imagem. Em vez de criar um componente enorme que abrange todas essas funcionalidades, podemos usar o padrão de composição para criar componentes menores e mais especializados e, em seguida, combiná-los para formar o Card.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import React from 'react';

// Componente reutilizável para o título
const Title = ({ children }) => <h2>{children}</h2>;

// Componente reutilizável para o texto
const Text = ({ children }) => <p>{children}</p>;

// Componente reutilizável para a imagem
const Image = ({ src, alt }) => <img src={src} alt={alt} style= />;

// Componente Card que utiliza os componentes acima por composição
const Card = ({ title, text, imageSrc, imageAlt }) => (
  <div className="card">
    <Title>{title}</Title>
    <Text>{text}</Text>
    <Image src={imageSrc} alt={imageAlt} />
  </div>
);

// Exemplo de uso do componente Card
const App = () => {
  return (
    <div>
      <Card
        title="Título do Card"
        text="Este é um exemplo de texto dentro do Card."
        imageSrc="https://via.placeholder.com/150"
        imageAlt="Imagem de exemplo"
      />
    </div>
  );
};

export default App;

Exemplo um pouco mais complexo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
interface CustomButtonProps extends React.ComponentProps<'button'> {
  variant?: 'primary' | 'secondary';
  size?: 'small' | 'large';
}

export const CustomButton: React.FC<CustomButtonProps> = ({
  variant = 'primary',
  size = 'medium',
  className,
  children,
  ...props
}) => {
  const classes = `btn ${variant} ${size} ${className}`;

  return (
    <button className={classes} {...props}>
      {children}
    </button>
  );
};

export const CustomIcon: React.FC<React.ComponentProps<typeof Icon>> = (props) => {
  return <Icon {...props} />;
};

export const CustomText: React.FC<React.ComponentProps<'span'>> = ({
  children,
  ...props
}) => {
  return <span {...props}>{children}</span>;
};

export const CustomCta = {
  Button: CustomButton,
  Icon: CustomIcon,
  Text: CustomText,
};

E com o componente criado, você poderá criar diferentes tipos de botões, como o botão de Like:

1
2
3
4
5
6
7
8
9
10
11
12
13
import React from 'react';
import { CustomCta } from './components/CustomCta'; 

const CtaButtonWithIcon = () => {
  return (
    <CustomCta.Button variant="secondary" onClick={() => alert('Botão Clicado')}>
      <CustomCta.Icon icon="heart" />
      <CustomCta.Text>Like</CustomCta.Text>
    </CustomCta.Button>
  );
};

export default CtaButtonWithIcon;

Botão favorito:

1
2
3
4
5
6
7
8
9
10
11
12
13
import React from 'react';
import { CustomCta } from './components/CustomCta'; 

const CustomizedCtaButton = () => {
  return (
    <CustomCta.Button variant="primary" onClick={() => alert('Botão Clicado')}>
      <CustomCta.Icon icon="star" />
      <CustomCta.Text>Favorito</CustomCta.Text>
    </CustomCta.Button>
  );
};

export default CustomizedCtaButton;

Sem o padrão de Composition, o mesmo componente poderia ser criado da seguinte forma:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import React from 'react';

const CustomCta = ({ variant = 'primary', onClick, icon, text }) => {
  let iconElement = null;
  if (icon) {
    iconElement = <span className={`icon icon-${icon}`} />;
  }

  return (
    <button className={`btn ${variant}`} onClick={onClick}>
      {iconElement}
      {text}
    </button>
  );
};

export default ButtonWithIconAndText;

Apesar de que nesse exemplo a forma sem o pattern Composition fique bem fácil de entender, a ideia é refletir uma outra forma de criar componentes que são mais fáceis de estender ou modificar. Possivelmente se o componente de CustomCta evoluísse, muitas props seriam passadas para ele, dificultando a manutenção.

Conclusão

A escolha entre usar o composition pattern e criar um componente de botão sem o composition pattern depende das necessidades específicas do seu projeto, do seu estilo de codificação e das preferências da sua equipe. Vamos considerar algumas vantagens e desvantagens de cada abordagem:

Composition Pattern:

Vantagens:

  • Modularidade: Permite criar componentes mais modulares e reutilizáveis, facilitando a construção e a manutenção de interfaces complexas.
  • Flexibilidade: Componentes individuais podem ser combinados de várias maneiras para criar uma variedade de interfaces.
  • Separação de Responsabilidades: Cada componente tem uma única responsabilidade, facilitando a compreensão e a manutenção do código.

Desvantagens:

  • Complexidade Adicional: Pode exigir um pouco mais de trabalho para criar e gerenciar vários componentes em comparação com uma abordagem menos modular.
  • Overhead de Abstração: Em alguns casos, a criação de muitos componentes pequenos pode levar a um aumento na complexidade e no overhead de abstração.

Sem Composition Pattern:

Vantagens:

  • Simplicidade: Pode ser mais simples e direto criar componentes sem dividir a funcionalidade em partes menores.
  • Menos Overhead: Menos abstrações e componentes podem levar a menos overhead de gerenciamento.

Desvantagens:

  • Menos Modularidade: Componentes podem ser menos modulares e reutilizáveis, tornando mais difícil a manutenção de interfaces complexas.

  • Dificuldade em Extensão: Pode ser mais difícil estender ou modificar componentes sem quebrar outras partes do código.

Em geral, o composition pattern é amplamente preferido na comunidade React devido à sua capacidade de criar componentes mais flexíveis e modulares. No entanto, a abordagem sem composition pattern pode ser adequada em casos simples ou em projetos onde a simplicidade é mais valorizada do que a flexibilidade e a reutilização.