This content originally appeared on DEV Community and was authored by José Lucas Panizio
đ§© TL;DR
OuseEnhanceChildrenĂ© um hook customizado que permite injetar ou mesclar props do componente pai em seus filhos â ideal para compound components sem precisar usar Context API.
â Sem boilerplate, com tipagem forte e suporte a hierarquias aninhadas.
đ Veja o projeto completo no GitHub â
Quem jĂĄ criou um compound component em React sabe que compartilhar props entre o componente pai e seus filhos pode ser um processo moroso.
A abordagem mais comum Ă© usar a Context API, mas, sinceramente... sinto que estou usando uma bazuca pra matar uma formiga (overkill). Tudo o que eu queria era que os subcomponentes do Root tivessem acesso Ă s mesmas props â sem precisar criar contextos, providers e hooks para cada caso.
đ« O problema
Existem basicamente duas formas tradicionais de compartilhar props:
Repetir as props manualmente nos subcomponentes, o que Ă© nada elegante.
Usar Context API, o que Ă© poderoso, mas verboso e desnecessariamente complexo em muitos casos.
Se o Root recebe as props e envolve os subcomponentes, Ă© natural que eles tenham acesso a elas. Afinal, as props pertencem ao Root, e ele deveria ser capaz de âdistribuĂ-lasâ facilmente.
đ€ âMas por que nĂŁo passar as props direto nos filhos?â
Boa pergunta.
Em alguns casos, faz todo sentido â por exemplo, quando o Root nĂŁo usa essas props.
Mas pense em situaçÔes em que o Root também precisa delas.
Um caso clĂĄssico Ă© a prop id.
Todos os elementos React aceitam id, e ela costuma ser Ăștil em testes.
Seria Ăłtimo poder passar um Ășnico id para o Root e fazer com que cada subcomponente tivesse um ID derivado automaticamente, como:
<Root id="user">
<Root.Header /> // id="user-header"
<Root.Body /> // id="user-body"
<Root.Footer /> // id="user-footer"
</Root>
Esse mesmo raciocĂnio vale para className, disabled, atributos data-*, entre outros.
Sim, o Context resolveria.
Mas ele adiciona boilerplate, complexidade e atĂ© listeners internos de pub/sub â algo desnecessĂĄrio quando queremos apenas espelhar props.
đ§ A solução: useEnhanceChildren
Apresento o useEnhanceChildren, um custom hook que facilita a injeção de props do componente pai nos filhos.
Ele deve ser utilizado dentro do componente Root, e conta com dois modos de operação:
1. Modo broadcast
No modo broadcast, o hook injeta o mesmo conjunto de props em todos os filhos, de forma uniforme (exceto elementos HTML nativos como <div> e <span>).
const children = useEnhanceChildren(props.children, {
props: { disabled: true, className: 'root-child' }
});
2. Modo map
No modo map, vocĂȘ especifica quais props vĂŁo para quais filhos, utilizando o displayName de cada subcomponente.
const children = useEnhanceChildren(props.children, {
mapProps: {
Header: { color: 'blue' },
Footer: { color: 'gray' },
}
});
đ O displayName Ă© essencial nesse modo.
Ă por meio dele que o hook identifica qual subcomponente deve receber quais props:
function Header(props: { color?: string }) { /* ... */ }
Header.displayName = 'Header';
đ§© Type-safety com generics
VocĂȘ pode definir um generic para garantir que os nomes e as props estejam corretos, evitando erros de digitação e garantindo a inferĂȘncia de tipos adequada.
type MapProps = {
'Card.Header': { title?: string };
'Card.Footer': { description?: string };
};
const enhanced = useEnhanceChildren<MapProps>(children, {
mapProps: {
'Card.Header': { title: 'TĂtulo' },
'Card.Footer': { description: 'Descrição' },
// Erro de tipagem se vocĂȘ tentar algo errado, por exemplo:
// 'Body': { wrongProp: true } â
},
});
đ§± Exemplo completo
Para visualizar o useEnhanceChildren em ação dentro de um compound component real, veja o exemplo abaixo onde o componente Card injeta suas props (title, description) diretamente nos filhos via useEnhanceChildren â sem Context, sem prop drilling manual.
import type { ReactNode, ComponentPropsWithoutRef } from 'react';
import { useEnhanceChildren } from '@/hooks/useEnhanceChildren';
type CardProps = {
title: string;
description: string;
children: ReactNode; // children serĂĄ automaticamente omitido pelo hook
};
/*
type CardMapProps = {
'Card.Header': { title: string };
'Card.Footer': { description: string };
};
*/
export function Card({ children, ...props }: CardProps) {
const enhancedChildren = useEnhanceChildren<CardProps>(children, {
props, // modo broadcast
});
/*
const enhancedChildren = useEnhanceChildren(children, {
mapProps: { // modo map
'Card.Header': { title: props.title },
'Card.Footer=': { description: props.description },
},
});
*/
return (
<div>
{enhancedChildren}
</div>
);
}
// ----------------------
// Card.Header
// ----------------------
type CardHeaderProps = ComponentPropsWithoutRef<'header'> & {
title?: string;
};
Card.Header = Object.assign(
({ title, ...rest }: CardHeaderProps) => (
<header {...rest}>
{title}
</header>
),
{ displayName: 'Card.Header' },
);
// ----------------------
// Card.Body
// ----------------------
type CardBodyProps = ComponentPropsWithoutRef<'main'>;
Card.Body = Object.assign(
({ children, ...rest }: CardBodyProps) => (
<main {...rest}>
{children}
</main>
),
{ displayName: 'Card.Body' },
);
// ----------------------
// Card.Footer
// ----------------------
type CardFooterProps = ComponentPropsWithoutRef<'footer'> & {
description?: string;
};
Card.Footer = Object.assign(
({ description, ...rest }: CardFooterProps) => (
<footer {...rest}>
{description}
</footer>
),
{ displayName: 'Card.Footer' },
);
E seu uso na prĂĄtica ficaria assim:
<Card title="TĂtulo principal" description="Descrição resumida">
<Card.Header />
<Card.Body>
<p>ConteĂșdo interno do card.</p>
</Card.Body>
<Card.Footer />
</Card>
Recursos e comportamento
â
Recursividade: percorre hierarquias aninhadas â netos, bisnetos e tataranetos tambĂ©m recebem as props.
â
PrecedĂȘncia correta: props passadas diretamente nos filhos tĂȘm prioridade (nĂŁo sĂŁo sobrescritas).
â
Ignora elementos HTML nativos.
â
Suporte a TypeScript forte: overloads e uniĂŁo discriminada para os modos map e broadcast.
â
Memoização interna: evita re-renderizaçÔes desnecessårias.
ConclusĂŁo
O useEnhanceChildren nasceu da necessidade de simplificar algo bem especĂfico: compartilhar props do Root com seus filhos em compound components â sem recorrer Ă complexidade da Context API.
Mas, embora tenha sido pensado para esse cenĂĄrio, acredito que ele pode ser Ăștil em outros contextos onde hĂĄ herança natural de props. E, claro, ainda hĂĄ espaço para evoluir â tanto em recursos quanto em ergonomia.
O hook não substitui o Context, mas preenche com elegùncia o espaço entre o prop drilling manual e o context overkill.
Se vocĂȘ trabalha com compound components, recomendo experimentar â e, por que nĂŁo, contribuir com melhorias.
đŹ DĂșvidas, crĂticas e sugestĂ”es tambĂ©m sĂŁo muito bem-vindas.
đ Veja o projeto completo no GitHub â
This content originally appeared on DEV Community and was authored by José Lucas Panizio
José Lucas Panizio | Sciencx (2025-11-02T20:24:26+00:00) Compartilhando props em Compound Components sem usar Context API. Retrieved from https://www.scien.cx/2025/11/02/compartilhando-props-em-compound-components-sem-usar-context-api/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.
