Busca en todas las páginas de la documentación
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
// Un componente headless que gestiona la posición del ratón
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>El ratón está en ({x}, {y})</p>}
/>Cuándo usarlo: Cuando un componente encapsula comportamiento reutilizable o lógica de estado y el consumidor necesita controlar qué se renderiza. También es útil para componentes de interfaz headless.
import { useState, useRef, useEffect, type ReactNode } from "react";
// Componente headless disclosure 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 - control completo sobre el renderizado
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>
);
}Lo que esto demuestra:
ReactNode.children (children: (props) => ReactNode) es la forma más común, a veces llamada "function-as-children".| Parámetro | Tipo | Propósito |
|---|---|---|
render o children | (state: T) => ReactNode | Función que el consumidor proporciona para controlar el renderizado |
| Estado interno | Varía | Pasado como argumento a la función de renderizado |
| Manejadores de eventos | () => void etc. | Proporcionados al consumidor para vincularse a sus propios elementos |
| Props ARIA | Objetos de expansión | Atributos de accesibilidad pre-construidos para elementos del consumidor |
Render prop con nombre - útil cuando necesitas múltiples ranuras de renderizado:
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>
);
}Patrón de colección de props - agrupa props relacionados en objetos que se puedan expandir:
// En lugar de props individuales:
// onClick={toggle} aria-expanded={isOpen} aria-controls={id}
// Proporciona una colección:
// {...triggerProps}children: (item: T) => ReactNode.Nueva función en cada renderizado - Los render props en línea crean una nueva función en cada renderizado, lo que puede interferir con React.memo. Solución: Extrae la función de renderizado a una referencia estable con useCallback si el componente padre está memoizado.
Infierno de envolventes - Anidar múltiples componentes de render prop crea una indentación profunda. Solución: Extrae el comportamiento de cada componente de render prop en un custom hook. La mayoría de patrones de render prop se pueden convertir a hooks.
Retornar fragmentos sin claves - Cuando render props retornan listas, cada elemento necesita una clave. Solución: Asegúrate de que el consumidor retorna elementos con clave cuando renderiza listas.
Romper las reglas de hooks dentro de render props - No puedes llamar hooks dentro de la función de renderizado pasada a un render prop. Solución: Si se necesitan hooks, extrae un componente separado y pasa datos como props.
| Enfoque | Compensación |
|---|---|
| Render props | Control completo de renderizado; puede llevar a anidamiento |
| Custom hooks | API más limpia; no puede encapsular estructura JSX |
| Composición (ranuras) | Más simple; el contenido de ranura no puede acceder al estado interno |
| Componentes de orden superior | Agrega comportamiento de forma transparente; más difícil de tipificar correctamente |
| Componentes compuestos | Múltiples elementos relacionados comparten estado implícitamente |
ReactNode.children es la función de renderizado: children: (props) => ReactNode.// En lugar de pasar props individuales:
// onClick={toggle} aria-expanded={isOpen} aria-controls={id}
// Proporciona un objeto que se pueda expandir:
// {...triggerProps}<DataTable
data={rows}
renderHeader={() => <tr><th>Nombre</th></tr>}
renderRow={(item) => <tr><td>{item.name}</td></tr>}
renderEmpty={() => <p>Sin datos</p>}
/>children funciona cuando solo hay un área de renderizado.useCallback si el componente padre está 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 del array items y lo aplica en el argumento de la función de renderizado.DisclosureRenderProps) permite que los consumidores tipifiquen sus funciones de renderizado externamente.triggerProps y contentProps) para que el consumidor los expanda.Revisado por Chris St. John·Última actualización: 16 jul 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥