Busque em todas as páginas da documentação
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
// HOC que adiciona controle de autenticação
function withAuth<P extends object>(
WrappedComponent: React.ComponentType<P>
) {
function AuthenticatedComponent(props: P) {
const { user, isLoading } = useAuth();
if (isLoading) return <LoadingSpinner />;
if (!user) return <Navigate to="/login" />;
return <WrappedComponent {...props} />;
}
AuthenticatedComponent.displayName =
`withAuth(${WrappedComponent.displayName ?? WrappedComponent.name ?? "Component"})`;
return AuthenticatedComponent;
}
// Uso
const ProtectedDashboard = withAuth(Dashboard);Quando usar isso: Quando você precisar aplicar a mesma preocupação transversal (autenticação, logging, temas, busca de dados) a muitos componentes sem modificá-los. Menos comum hoje em dia graças aos hooks, mas ainda útil para wrappers em nível de rota e integração com bibliotecas de terceiros.
import { useEffect, useRef, type ComponentType } from "react";
// HOC que rastreia a visibilidade do componente e relata análises
function withVisibilityTracking<P extends { id: string }>(
WrappedComponent: ComponentType<P>,
eventName: string
) {
function TrackedComponent(props: P) {
const ref = useRef<HTMLDivElement>(null);
const reported = useRef(false);
useEffect(() => {
const element = ref.current;
if (!element) return;
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting && !reported.current) {
reported.current = true;
analytics.track(eventName, { componentId: props.id });
}
},
{ threshold: 0.5 }
);
observer.observe(element);
return () => observer.disconnect();
}, [props.id]);
return (
<div ref={ref}>
<WrappedComponent {...props} />
</div>
);
}
TrackedComponent.displayName =
`withVisibilityTracking(${WrappedComponent.displayName ?? WrappedComponent.name ?? "Component"})`;
return TrackedComponent;
}
// Uso
interface ProductCardProps {
id: string;
name: string;
price: number;
}
function ProductCard({ id, name, price }: ProductCardProps) {
return (
<div className="p-4 border rounded">
<h3>{name}</h3>
<p>${price}</p>
</div>
);
}
const TrackedProductCard = withVisibilityTracking(ProductCard, "product_viewed");
// Em uma página
function ProductGrid({ products }: { products: ProductCardProps[] }) {
return (
<div className="grid grid-cols-3 gap-4">
{products.map((p) => (
<TrackedProductCard key={p.id} {...p} />
))}
</div>
);
}O que isso demonstra:
P extends { id: string }) garante que o componente envolvido tenha as props necessáriasdisplayName definido para depuração do React DevToolsProductCard original permanece puro e testável por si sóenhance(Component) => EnhancedComponent.withAuth(withTheme(withAnalytics(Component))).| Parâmetro | Tipo | Propósito |
|---|---|---|
WrappedComponent | ComponentType<P> | O componente a ser aprimorado |
| Config (opcional) | Varia | Configuração para o comportamento do HOC |
| Valor de retorno | ComponentType<P> (ou modificado) | Um novo componente com comportamento adicionado |
HOC de injeção de props - adiciona novas props ao componente envolvido:
interface WithThemeProps {
theme: Theme;
}
function withTheme<P extends WithThemeProps>(
WrappedComponent: ComponentType<P>
) {
function ThemedComponent(props: Omit<P, keyof WithThemeProps>) {
const theme = useTheme();
return <WrappedComponent {...(props as P)} theme={theme} />;
}
ThemedComponent.displayName =
`withTheme(${WrappedComponent.displayName ?? WrappedComponent.name})`;
return ThemedComponent;
}HOCs Compostos - combinam múltiplos aprimoramentos:
// Composição manual
const EnhancedComponent = withAuth(withTheme(withAnalytics(BaseComponent)));
// Com uma utilidade compose
import { compose } from "redux"; // ou escreva a sua
const enhance = compose(withAuth, withTheme, withAnalytics);
const EnhancedComponent = enhance(BaseComponent);ComponentType<P> para aceitar componentes de função e de classe.Omit<P, keyof InjectedProps> para remover props injetadas da API externa.P extends object no mínimo para evitar tipos primitivos.displayName para cada HOC para tornar o React DevTools utilizável.React.forwardRef dentro de HOCs se o componente envolvido precisar de encaminhamento de ref.Encaminhamento de Ref - Refs não passam por HOCs automaticamente porque ref não é uma prop regular. Correção: Use React.forwardRef dentro do HOC wrapper.
Métodos estáticos perdidos - Propriedades estáticas no componente original não são copiadas para o wrapper. Correção: Use hoist-non-react-statics ou copie manualmente os estáticos necessários.
Colisões de nomes de props - Se o HOC injetar uma prop com o mesmo nome de uma prop existente, ela sobrescreve silenciosamente. Correção: Coloque em namespace as props injetadas ou use um prefixo único.
Recriando o HOC a cada renderização - Chamar um HOC dentro do corpo de um componente cria um novo tipo de componente a cada renderização, destruindo todo o estado. Correção: Sempre chame HOCs no escopo do módulo ou em um useMemo com extremo cuidado.
Dificuldade de depuração - HOCs profundamente aninhados criam árvores de componentes longas no DevTools. Correção: Defina displayName em cada HOC e considere se hooks seriam mais claros.
| Abordagem | Trade-off |
|---|---|
| Higher-order components | Aprimoramento transparente; a tipagem é complexa, a depuração é mais difícil |
| Custom hooks | Mais simples, composable, melhores tipos; não pode envolver a renderização |
| Render props | Fluxo de dados explícito; mais verboso no local de chamada |
| Middleware (Next.js) | Melhor para preocupações em nível de rota como autenticação em aplicativos Next.js |
| Decorators (stage 3) | Açúcar sintático; ainda não amplamente suportado no ecossistema React |
enhance(Component) => EnhancedComponent.// Composição manual
const Enhanced = withAuth(withTheme(withAnalytics(Base)));
// Com uma utilidade compose
const enhance = compose(withAuth, withTheme, withAnalytics);
const Enhanced = enhance(Base);compose.withAnalytics é aplicado primeiro, depois withTheme, depois withAuth.displayName, o React DevTools mostra o componente wrapper como "Anonymous" ou com o nome interno do wrapper.displayName para withAuth(Dashboard) torna a árvore de componentes legível.displayName ou name do componente envolvido.ref não é uma prop regular no React; ele é tratado especialmente e removido antes de chegar ao componente envolvido.React.forwardRef dentro do HOC para encaminhar refs ao componente envolvido.interface WithThemeProps { theme: Theme; }
function withTheme<P extends WithThemeProps>(
WrappedComponent: ComponentType<P>
) {
function Themed(props: Omit<P, keyof WithThemeProps>) {
const theme = useTheme();
return <WrappedComponent {...(props as P)} theme={theme} />;
}
return Themed;
}Omit<P, keyof InjectedProps> para remover props injetadas da API externa.P extends InjectedProps para que o TypeScript saiba que o componente envolvido aceita essas props.Omit<P, keyof InjectedProps> remove os nomes das props injetadas do tipo que o consumidor vê.theme ao usar withTheme(Component) porque o HOC o fornece.P extends object no mínimo para evitar parâmetros de tipo primitivo.hoist-non-react-statics ou copie manualmente os estáticos necessários.getStaticProps.themeConfig em vez de config) ou use um prefixo único.Revisado por Chris St. John·Última atualização: 16 de jul. de 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥