Busque em todas as páginas da documentação
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
// Um componente headless que gerencia a posição do mouse
function MouseTracker({ render }: {
render: (position: { x: number; y: number }) => React.ReactNode;
}) {
const [position, setPosition] = useState({ x: 0, y: 0 });
const handleMouseMove = (e: React.MouseEvent) => {
setPosition({ x: e.clientX, y: e.clientY });
};
return <div onMouseMove={handleMouseMove}>{render(position)}</div>;
}
// Uso
<MouseTracker
render={({ x, y }) => <p>O mouse está em ({x}, {y})</p>}
/>Quando usar isso: Quando um componente encapsula comportamento ou lógica de estado reutilizáveis e o consumidor precisa controlar o que é renderizado. Também útil para componentes de UI headless.
import { useState, useRef, useEffect, type ReactNode } from "react";
// Componente de divulgação headless usando render prop
interface DisclosureRenderProps {
isOpen: boolean;
toggle: () => void;
open: () => void;
close: () => void;
triggerProps: {
onClick: () => void;
"aria-expanded": boolean;
"aria-controls": string;
};
contentProps: {
id: string;
role: "region";
hidden: boolean;
};
}
function Disclosure({
id,
defaultOpen = false,
children,
}: {
id: string;
defaultOpen?: boolean;
children: (props: DisclosureRenderProps) => ReactNode;
}) {
const [isOpen, setIsOpen] = useState(defaultOpen);
const contentId = `${id}-content`;
const api: DisclosureRenderProps = {
isOpen,
toggle: () => setIsOpen((prev) => !prev),
open: () => setIsOpen(true),
close: () => setIsOpen(false),
triggerProps: {
onClick: () => setIsOpen((prev) => !prev),
"aria-expanded": isOpen,
"aria-controls": contentId,
},
contentProps: {
id: contentId,
role: "region",
hidden: !isOpen,
},
};
return <>{children(api)}</>;
}
// Uso - controle total sobre a renderização
function FAQ({ items }: { items: { q: string; a: string }[] }) {
return (
<div className="space-y-2">
{items.map((item, i) => (
<Disclosure key={i} id={`faq-${i}`}>
{({ triggerProps, contentProps, isOpen }) => (
<div className="border rounded-lg">
<button
{...triggerProps}
className="w-full text-left p-4 font-medium flex justify-between"
>
{item.q}
<span>{isOpen ? "−" : "+"}</span>
</button>
<div {...contentProps} className="px-4 pb-4 text-gray-600">
{item.a}
</div>
</div>
)}
</Disclosure>
))}
</div>
);
}O que isso demonstra:
ReactNode.children (children: (props) => ReactNode) é a forma mais comum, às vezes chamada de "function-as-children".| Parâmetro | Tipo | Propósito |
|---|---|---|
render ou children | (state: T) => ReactNode | Função que o consumidor fornece para controlar a renderização |
| Estado interno | Varia | Passado como argumento para a função de renderização |
| Manipuladores de eventos | () => void etc. | Fornecidos ao consumidor para vinculação aos seus próprios elementos |
| Props ARIA | Objetos espalhados | Atributos de acessibilidade pré-construídos para elementos do consumidor |
Render prop nomeada - útil quando você precisa de vários slots de renderização:
function DataTable<T>({
data,
renderHeader,
renderRow,
renderEmpty,
}: {
data: T[];
renderHeader: () => ReactNode;
renderRow: (item: T, index: number) => ReactNode;
renderEmpty: () => ReactNode;
}) {
if (data.length === 0) return <>{renderEmpty()}</>;
return (
<table>
<thead>{renderHeader()}</thead>
<tbody>{data.map((item, i) => renderRow(item, i))}</tbody>
</table>
);
}Padrão de coleção de props - agrupa props relacionadas em objetos que podem ser espalhados:
// Em vez de props individuais:
// onClick={toggle} aria-expanded={isOpen} aria-controls={id}
// Fornece uma coleção:
// {...triggerProps}children: (item: T) => ReactNode.Nova função a cada renderização - Render props inline criam uma nova função a cada renderização, o que pode interferir com React.memo. Correção: Extraia a função de renderização para uma referência estável com useCallback se o componente pai for memoizado.
Inferno de wrappers - Aninhar múltiplos componentes de render prop cria indentação profunda. Correção: Extraia hooks personalizados para o comportamento em vez disso. A maioria dos padrões de render prop pode ser convertida em hooks.
Retornando fragmentos sem chaves - Quando render props retornam listas, cada item precisa de uma chave. Correção: Certifique-se de que o consumidor retorne elementos com chaves ao renderizar listas.
Quebrando regras de hooks dentro de render props - Você não pode chamar hooks dentro da função de renderização passada para uma render prop. Correção: Se hooks forem necessários, extraia um componente separado e passe os dados como props.
| Abordagem | Trade-off |
|---|---|
| Render props | Controle total de renderização; pode levar a aninhamento |
| Hooks personalizados | API mais limpa; não pode encapsular a estrutura JSX |
| Composição (slots) | Mais simples; o conteúdo do slot não pode acessar o estado interno |
| Componentes de ordem superior | Adiciona comportamento transparentemente; mais difícil de tipar corretamente |
| Componentes compostos | Múltiplos elementos relacionados compartilham estado implicitamente |
ReactNode.children é a função de renderização: children: (props) => ReactNode.// Em vez de passar props individuais:
// onClick={toggle} aria-expanded={isOpen} aria-controls={id}
// Fornece um objeto que pode ser espalhado:
// {...triggerProps}<DataTable
data={rows}
renderHeader={() => <tr><th>Nome</th></tr>}
renderRow={(item) => <tr><td>{item.name}</td></tr>}
renderEmpty={() => <p>Nenhum dado</p>}
/>children funciona quando há apenas uma área de renderização.useCallback se o componente pai for memoizado.interface ListProps<T> {
items: T[];
children: (item: T, index: number) => ReactNode;
}
function List<T>({ items, children }: ListProps<T>) {
return <ul>{items.map((item, i) => <li key={i}>{children(item, i)}</li>)}</ul>;
}T do array items e o impõe no argumento da função de renderização.DisclosureRenderProps) permite que os consumidores digitem suas funções de renderização externamente.triggerProps e contentProps) para o consumidor espalhar.Revisado por Chris St. John·Última atualização: 16 de jul. de 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥