O Factory Pattern em React resolve um problema antigo do front-end. Afinal, como criar componentes dinâmicos sem encher o código de ifs e switches? Portanto, neste guia você vai entender o que é, quando vale a pena usar e como aplicar a técnica com exemplos práticos em TSX. Além disso, vamos comparar a estratégia com outras abordagens modernas do ecossistema. Por fim, veja boas práticas e armadilhas para evitar em projetos reais. O conteúdo é direto e mira desenvolvedores de nível intermediário.

O que é o Factory Pattern em React?

O Factory Pattern em React é uma adaptação do clássico Factory Method, descrito originalmente pelo Gang of Four. Em essência, trata-se de uma função que recebe um parâmetro e devolve um componente já configurado. Dessa forma, a lógica de escolha fica isolada num único ponto do código. Consequentemente, o restante da aplicação consome um componente pronto, sem precisar conhecer detalhes internos da decisão.

Em termos formais, factory é um padrão criacional. Ou seja, ele cuida da criação de objetos sem expor a instância concreta ao cliente. No React, isso ganha um sabor especial. Afinal, componentes também são objetos JavaScript no fim das contas. Por isso, encaixam perfeitamente nessa abordagem funcional. Inclusive, o próprio JSX é apenas açúcar sintático sobre chamadas de função.

Vale lembrar uma analogia útil. Pense numa lanchonete. Você pede um lanche pelo número do combo, e a cozinha entrega o pacote pronto. Você não precisa montar pão, queijo e hambúrguer separadamente. Da mesma forma, a factory esconde a montagem e devolve o componente certo conforme o pedido recebido.

Quando usar o Factory Pattern em React?

Diagrama do padrão Factory em React — fluxo de criação de componentes via função fábrica
Diagrama do fluxo Factory em React: input descreve o tipo → função fábrica decide qual componente retornar.

Nem todo projeto precisa do Factory Pattern em React. Por isso, é importante reconhecer os sinais certos. Primeiro, observe se você tem muitos componentes parecidos, mas com pequenas variações. Em seguida, veja se a escolha entre eles depende de uma prop, configuração ou resposta de API. Se ambas as respostas forem sim, factory faz sentido.

Veja alguns cenários típicos. Por exemplo, formulários dinâmicos que renderizam campos diferentes conforme o schema. Da mesma forma, dashboards com widgets variados decididos pelo back-end. Inclusive, sistemas de design com botões em múltiplas variantes se beneficiam bastante. Em todos esses casos, a abordagem reduz acoplamento e melhora a legibilidade do código.

Por outro lado, evite factory quando há apenas duas ou três variações simples. Nesse caso, um operador ternário resolve. Adicionalmente, fuja do padrão se ele aumentar a complexidade sem ganho real. Afinal, o objetivo é simplificar. Portanto, sempre questione se a abstração paga o preço de aprender mais uma camada.

Como implementar o Factory Pattern em React: exemplo prático

Agora vamos ao código. Primeiro, considere um caso clássico: renderizar diferentes tipos de botão. Veja a seguir uma versão ingênua, baseada em switch, e logo depois a alternativa limpa.

Code
// Anti-pattern: lógica espalhada com switch
function Button({ variant, children, onClick }) {
  switch (variant) {
    case 'primary':
      return <button className="btn-primary" onClick={onClick}>{children}</button>
    case 'secondary':
      return <button className="btn-secondary" onClick={onClick}>{children}</button>
    case 'ghost':
      return <button className="btn-ghost" onClick={onClick}>{children}</button>
    default:
      return null
  }
}

Esse código funciona, mas escala mal. A cada nova variante, o switch cresce. Além disso, testar fica chato. Por isso, vamos refatorar usando factory. Observe abaixo a versão limpa:

TypeScript
// Factory pattern: registro central de variantes
type ButtonVariant = 'primary' | 'secondary' | 'ghost'

const PrimaryButton = ({ children, onClick }: ButtonProps) => (
  <button className="btn-primary" onClick={onClick}>{children}</button>
)
const SecondaryButton = ({ children, onClick }: ButtonProps) => (
  <button className="btn-secondary" onClick={onClick}>{children}</button>
)
const GhostButton = ({ children, onClick }: ButtonProps) => (
  <button className="btn-ghost" onClick={onClick}>{children}</button>
)

const buttonRegistry: Record<ButtonVariant, React.FC<ButtonProps>> = {
  primary: PrimaryButton,
  secondary: SecondaryButton,
  ghost: GhostButton,
}

export function buttonFactory(variant: ButtonVariant) {
  return buttonRegistry[variant] ?? PrimaryButton
}

Note como ficou simples. Cada variante vira um componente isolado. Em seguida, um registro mapeia chaves para componentes. Por fim, a função buttonFactory apenas devolve o componente certo. Ademais, adicionar uma nova variante exige só duas linhas: criar o componente e registrar no objeto.

Agora veja o consumo no app. Repare como o uso fica declarativo:

TypeScript
// Uso real: clean, sem ifs
import { buttonFactory } from './buttonFactory'

export function CTASection({ type }: { type: ButtonVariant }) {
  const Button = buttonFactory(type)
  return (
    <section>
      <Button onClick={() => console.log('clicked')}>
        Confirmar
      </Button>
    </section>
  )
}

Esse modelo escala bem para outros casos. Inclusive, dá pra usar a mesma ideia em ícones, modais, cards e widgets diversos. Por exemplo, a Zalando adotou factory para renderizar páginas inteiras de e-commerce. Da mesma forma, qualquer projeto com renderização orientada por dados se beneficia muito.

Outro ponto vale destaque. Em times grandes, factory facilita testes automatizados. Afinal, basta mockar o registro para forçar variantes específicas. Inclusive, isso reduz drasticamente o tempo de setup de testes unitários. Da mesma forma, mocks tornam-se previsíveis e fáceis de manter ao longo do tempo.

Factory Pattern vs outros Design Patterns no React

Comparação Factory vs outros design patterns no React
Quando Factory ganha de Strategy/Builder/Abstract Factory no contexto de UI React.

O Factory Pattern em React não vive sozinho. Pelo contrário, ele convive com outras técnicas comuns no ecossistema. A seguir, veja como ele se compara aos principais concorrentes diretos. Cada um tem seu papel, mas o objetivo é diferente em cada caso.

Primeiro, o Higher-Order Component (HOC) também envolve criação. Contudo, HOC adiciona comportamento a um componente existente. Já factory escolhe qual componente entregar. Em segundo lugar, o Compound Component, popularizado por Kent C. Dodds, foca em composição via children. Por outro lado, factory foca em seleção via input.

Em terceiro lugar, render props entregam controle ao consumidor. Em contraste, factory mantém o controle internamente. Por fim, o Strategy Pattern é parente próximo. Aliás, ambos trocam algoritmos em tempo de execução. A diferença está no foco: strategy troca comportamento, factory troca a instância. Em projetos grandes, é comum combinar dois ou três desses padrões num mesmo módulo.

Para quem quer aprofundar nesses fundamentos, recomendo dois caminhos. Primeiro, ler o capítulo do Patterns.dev. Em seguida, explorar os livros sobre arquitetura de software que organizei aqui no blog. Adicionalmente, a técnica dialoga muito com Domain-Driven Design, principalmente na camada de aplicação.

Boas práticas ao aplicar o Factory Pattern em React

Aplicar o Factory Pattern em React com qualidade exige disciplina. Portanto, siga algumas regras simples. Primeiro, mantenha o registro de componentes num arquivo dedicado. Em seguida, tipar com TypeScript transforma a factory numa fortaleza segura. Afinal, o compilador acusa imediatamente qualquer variante esquecida no registro.

Adicionalmente, exporte o tipo das chaves do registro. Dessa forma, consumidores reaproveitam a mesma união literal. Inclusive, isso evita strings mágicas espalhadas pelo código. Por exemplo, em vez de digitar 'primary' em vários arquivos, importe o tipo ButtonVariant. Assim, refactors viram quase automáticos. Logo, o time inteiro ganha em consistência sem esforço extra.

Outra prática importante envolve o fallback. Sempre defina um componente padrão para entradas desconhecidas. Caso contrário, a aplicação pode quebrar silenciosamente. Da mesma forma, registre logs quando um fallback acionar. Consequentemente, fica fácil identificar variantes faltantes em produção.

Por fim, lembre da performance. A documentação oficial do React recomenda React.memo em componentes que recebem props estáveis. Portanto, considere envolver os componentes do registro em memo. Dessa forma, evita renders desnecessários quando a função devolve sempre a mesma instância.

Erros comuns e como evitar

Mesmo simples, esse padrão tem armadilhas. Inicialmente, muitos devs criam factories gigantes com dezenas de variantes. Isso vira anti-pattern rapidamente. Em seguida, esquecem o tipo das chaves, e o TypeScript perde força. Por outro lado, alguns abusam de lógica condicional dentro do componente devolvido, anulando o ganho original.

Três erros recorrentes

Primeiro, criar factories acopladas a fetch de dados. Nesse caso, separe: a função escolhe, hooks buscam. Em segundo, retornar JSX cru em vez de componentes nomeados. Consequentemente, perde-se nome no React DevTools. Por fim, importar todos os componentes mesmo sem usar. Portanto, considere lazy para variantes pesadas no bundle.

Adicionalmente, fique atento ao tamanho do bundle final. Cada componente registrado entra no build. Por isso, em apps grandes, use React.lazy com Suspense para carregar sob demanda. Em breve teremos artigo dedicado a Componentes em React, onde aprofundo essas técnicas de divisão de código modular.

Próximos passos

Em resumo, o Factory Pattern em React é uma ferramenta poderosa para componentes dinâmicos. Portanto, comece pequeno: identifique um switch repetitivo no seu código atual. Em seguida, extraia para um registro tipado. Por fim, refatore o consumo para usar a função factory. Dessa forma, o ganho fica visível já no primeiro commit do projeto.

Por fim, lembre que padrões servem ao código, não o contrário. Portanto, adapte a ideia ao seu contexto sem dogmas. Inclusive, comece testando em um único componente antes de espalhar. Assim, o time entende o ganho na prática. Logo depois, expanda para outros pontos do projeto com mais segurança e previsibilidade.

O Factory Pattern em React substitui o uso de switch e if?
Sim, esse padrão substitui ifs e switches em casos com muitas variantes. Contudo, para duas ou três opções simples, um ternário ainda é mais legível.
Posso usar factory com componentes carregados de forma lazy?
Sim. Basta envolver cada componente do registro com React.lazy e renderizar dentro de Suspense. Dessa forma, o bundle inicial fica menor.
Factory funciona bem com TypeScript?
Funciona muito bem. Inclusive, TypeScript potencializa o Factory Pattern em React ao validar todas as variantes do registro em tempo de compilação.
Qual a diferença entre factory e Higher-Order Component?
HOC envolve um componente para adicionar comportamento. Já factory escolhe qual componente entregar. Portanto, são padrões complementares, não concorrentes.
Preciso de uma biblioteca para usar esse padrão?
Não. Basta JavaScript ou TypeScript puro. O Factory Pattern em React é apenas uma técnica organizacional, sem dependências externas.