Executando verificação de segurança...
3

Aplicando Design Patterns Criacionais em Componentes React

Neste artigo, vamos construir exemplos práticos de creational design patterns aplicados ao código React. Embora existam inumeros padrões, muitos podem precisar de modelos reais para compreender sua aplicabilidade. Se você nunca ouviu falar sobre design patterns antes, sugiro dar uma olhada aqui para ter uma visão geral. Segue três exemplos que considero muito uteis para aplicações React.

Factory Method

Ele fornece uma maneira de abstrair o processo de criação de componentes React, tornando-o flexível e reutilizável.

type ProfileType = "ADMIN" | "BUYER" | "SELLER";

/* 
  nossos componentes definidos por perfil podem ter 
  sua complexidade isolada
*/
const ProfileSeller = () => <h4>seller profile</h4>;
const ProfileBuyer = () => <h4>buyer profile</h4>;
const ProfileAdmin = () => <h4>admin profile</h4>;

/* 
  Usando o tipo do usuario conseguimos criar o componente correto 
  aplicando o conceito de factory method
*/
function ProfileFactory(type: ProfileType) {
  const profilesType: Record<ProfileType, React.FC> = {
    ADMIN: () => <ProfileAdmin />,
    BUYER: () => <ProfileBuyer />,
    SELLER: () => <ProfileSeller />,
  };
  return profilesType[type];
}

/* 
  Agora renderizamos o componente
  nota-se que abstraimos completamante a complexidade de implementacao
*/
function Example(){
  const Profile = ProfileFactory("ADMIN");
  return (
    <div>
      <h3>My Profile</h3>
      <Profile />
    </div>
  );
}
export default Example;

Abstract Factory Method

Podemos usar esse pattern quando precisamos criar uma família de componentes que compartilhem características e comportamentos. Esse pattern vai nos dar uma flexibilidade incrível.

/*
  Caracteristicas compartilhas entre nossos componentes
  criamos as interface abaixo para garantir que
  nao vamos criar um componente invalido
*/
interface AbstractProfile {
  style: string;
  type: "ADMIN" | "BUYER" | "SELLER";
  goToProfile: () => void;
}
interface AbstractProfileFactory {
  createProfile: () => AbstractProfile;
}
interface ProfileProps {
  factory: AbstractProfileFactory;
}
/*
  Nossas funcoes fabricas abaixo criam
  componentes que compartilhar caracteristicas
  e garantimos a correta implementacao atraves de 
  interfaces.
*/
const AdminProfileFactory = (): AbstractProfileFactory => ({
  createProfile: () => ({
    style: "profile-admin",
    type: "ADMIN",
    goToProfile: () => location.assign("/perfil/admin"),
  }),
});

const BuyerProfileFactory = (): AbstractProfileFactory => ({
  createProfile: () => ({
    style: "profile-buyer",
    type: "BUYER",
    goToProfile: () => location.assign("/perfil/buyer"),
  }),
});

const SellerProfileFactory = (): AbstractProfileFactory => ({
  createProfile: () => ({
    style: "profile-seller",
    type: "SELLER",
    goToProfile: () => location.assign("/perfil/seller"),
  }),
});

/*
  Esse Componente tem a responsabilidade 
  de renderizar o comportamento padrao de qualquer
  componente que compartilhe o comportamento 
  definido nas interfaces
*/
function ProfileUser(props: ProfileProps) {
  const { factory } = props;
  const { style, type, goToProfile } = factory.createProfile();
  return (
    <div className={style}>
      <button onClick={() => goToProfile()}>{type}</button>
    </div>
  );
}
/*
  Agora podemos carregar nossos perfils 
  em qualquer contexto necessario
  Seguindo as interfaces podemos ate criar novos perfis
  com seguranca que nao vamos quebrar o comportamento esperado
*/
function Example() {
  return (
    <div>
      <ProfileUser factory={AdminProfileFactory()} />
      <ProfileUser factory={BuyerProfileFactory()} />
      <ProfileUser factory={SellerProfileFactory()} />
    </div>
  );
}
export default Example;

Builder

Podemos construir componentes complexos de forma modular e com configurações diferentes de acordo com contexto.

type UserType = "ADMIN" | "BUYER";
type HeaderProps = {
  userType: UserType;
};

/**
  Criamos nossos componentes que vao ser utilizados
  de acordo com perfil do usuario assim renderizamos 
  somente o necessario 
 */
const LinkMyProducts = () => <a>lista de produtos </a>;
const LinkMyWishList = () => <a>lista de desejos </a>;
const LinkManagerUsers = () => <a>gerenciar usuarios </a>;

/**
  Aqui aplicamos o builder pattern
 */
function MenuBuild() {
  const items: JSX.Element[] = [];
  // Funcao responsavel por criar nosso menu
  const build = () => <nav>{items.map((item) => item)}</nav>;

  // Adiciona partes do nosso menu de forma dinamica
  const addItem = (component: JSX.Element) => {
    items.push(component);
    return {
      addItem,
      build,
    };
  };
  return { addItem, build };
}

function Header({ userType }: HeaderProps): JSX.Element {
  /**
   Adicionando componentes padroes no menu
   esses podem ser acessos por qualquer perfil de usuario
 */
  const Menu = MenuBuild()
    .addItem(<LinkMyProducts />)
    .addItem(<LinkMyWishList />);

  // Nosso usuario admin tem acesso a um recurso exclusivo
  if (userType === "ADMIN") {
    Menu.addItem(<LinkManagerUsers />);
  }
  // aqui montamos o componente final que pode mudar baseado no perfil
  return Menu.build();
}

function Example() {
  return (
    <div>
      <Header userType="ADMIN" />
      <Header userType="BUYER" />
    </div>
  );
}

export default Example;

Bem eu espero que esses exemplos te ajudem a entender melhor esses padrões e aplicá-los em seus projetos React. Segue link com repositório dos exemplos Repo.

Carregando publicação patrocinada...
1
1

Massa a ideia e demonstrar aplicadade desses patterns logo vou trazer exemplos de patterns comportamentais tambem entre outros, muitas vezes aplicamos esses patterns so n damos conta que o estamos fazendo

1
const AdminProfileFactory = (): AbstractProfileFactory => ({
  createProfile: () => ({
    style: "profile-admin",
    type: "ADMIN",
    goToProfile: () => location.assign("/perfil/admin"),
  }),
});

const BuyerProfileFactory = (): AbstractProfileFactory => ({
  createProfile: () => ({
    style: "profile-buyer",
    type: "BUYER",
    goToProfile: () => location.assign("/perfil/buyer"),
  }),
});

const SellerProfileFactory = (): AbstractProfileFactory => ({
  createProfile: () => ({
    style: "profile-seller",
    type: "SELLER",
    goToProfile: () => location.assign("/perfil/seller"),
  }),
});```

Isso me pareceu muita repetição de código, no mínimo muito verboso.