En esta página
Props y Children
¿Qué son las props?
Las props (properties) son el mecanismo de React para pasar datos de un componente padre a un componente hijo. Son análogas a los parámetros de una función: el padre decide qué valores pasa, y el hijo los recibe como un objeto de solo lectura.
// El padre controla los valores
function App() {
return <Saludo nombre="María" formal={true} />;
}
// El hijo recibe y usa las props
function Saludo({ nombre, formal }: { nombre: string; formal: boolean }) {
const prefijo = formal ? 'Buenos días,' : '¡Hola,';
return <h1>{prefijo} {nombre}!</h1>;
}Las props son inmutables desde la perspectiva del hijo: un componente nunca debe modificar sus propias props. Si necesita cambiar un valor, eso es responsabilidad del estado (que veremos en la próxima lección).
Tipado de props con interfaces TypeScript
La forma correcta y escalable de tipar props es mediante interfaces:
interface PerfilProps {
// Props requeridas (sin ?)
nombre: string;
email: string;
avatarUrl: string;
// Props opcionales (con ?)
biografia?: string;
seguidores?: number;
verificado?: boolean;
}
function Perfil({
nombre,
email,
avatarUrl,
biografia = 'Sin biografía',
seguidores = 0,
verificado = false,
}: PerfilProps): React.JSX.Element {
return (
<div className="perfil">
<img src={avatarUrl} alt={`Foto de perfil de ${nombre}`} />
<h2>
{nombre}
{verificado && <span aria-label="verificado">✓</span>}
</h2>
<p>{email}</p>
<p>{biografia}</p>
<p>{seguidores.toLocaleString('es-ES')} seguidores</p>
</div>
);
}Destructuring de props
Hay dos formas de acceder a las props:
// Forma 1: object parameter
function Componente(props: MisProps) {
return <p>{props.nombre}</p>;
}
// Forma 2: destructuring directo (recomendada)
function Componente({ nombre, edad }: MisProps) {
return <p>{nombre}, {edad} años</p>;
}
// Destructuring con renombre de variable
function Componente({ nombre: nombreUsuario }: MisProps) {
return <p>{nombreUsuario}</p>;
}El destructuring en la firma de la función es la forma más idiomática en código React moderno porque hace explícitas las dependencias del componente.
Props de función y callbacks
Las props pueden ser funciones, lo que permite que los hijos comuniquen eventos a los padres:
interface FilaTablaProps {
item: { id: string; nombre: string; precio: number };
onEditar: (id: string) => void;
onEliminar: (id: string) => void;
onSeleccionar: (id: string, seleccionado: boolean) => void;
}
function FilaTabla({ item, onEditar, onEliminar, onSeleccionar }: FilaTablaProps) {
return (
<tr>
<td>{item.nombre}</td>
<td>${item.precio}</td>
<td>
<button type="button" onClick={() => onEditar(item.id)}>Editar</button>
<button type="button" onClick={() => onEliminar(item.id)}>Eliminar</button>
<input
type="checkbox"
onChange={(e) => onSeleccionar(item.id, e.target.checked)}
/>
</td>
</tr>
);
}El prop `children`
children es la prop especial que representa el contenido que se coloca entre las etiquetas de apertura y cierre de un componente. Permite la composición: el padre decide qué renderizar dentro del componente.
interface ContenedorProps {
children: React.ReactNode;
className?: string;
}
function Contenedor({ children, className = '' }: ContenedorProps) {
return (
<div className={`contenedor ${className}`}>
{children}
</div>
);
}
// Uso
<Contenedor className="destacado">
<h2>Título</h2>
<p>Cualquier contenido puede ir aquí</p>
<Boton etiqueta="Acción" />
</Contenedor>React.ReactNode es el tipo más flexible para children: acepta elementos JSX, strings, números, null, undefined, arrays y fragmentos. Es el tipo correcto en casi todos los casos.
Patrones de composición avanzados
Componentes compuestos (Compound Components)
Permiten una API más expresiva para componentes complejos:
interface AccordionProps {
children: React.ReactNode;
}
interface AccordionItemProps {
titulo: string;
children: React.ReactNode;
}
function Accordion({ children }: AccordionProps) {
return <div className="accordion">{children}</div>;
}
function AccordionItem({ titulo, children }: AccordionItemProps) {
return (
<details className="accordion-item">
<summary>{titulo}</summary>
<div className="accordion-contenido">{children}</div>
</details>
);
}
// Uso natural y expresivo
<Accordion>
<AccordionItem titulo="¿Cómo funciona?">
<p>Explicación detallada aquí…</p>
</AccordionItem>
<AccordionItem titulo="¿Cuánto cuesta?">
<p>Los precios varían según el plan.</p>
</AccordionItem>
</Accordion>Render Props
Permite que un componente delegue cómo renderizar al padre:
interface ListaConRenderProps<T> {
items: T[];
renderItem: (item: T, index: number) => React.ReactNode;
keyExtractor: (item: T) => string;
}
function ListaConRender<T>({ items, renderItem, keyExtractor }: ListaConRenderProps<T>) {
return (
<ul>
{items.map((item, index) => (
<li key={keyExtractor(item)}>{renderItem(item, index)}</li>
))}
</ul>
);
}
// Uso con tipo inferido
<ListaConRender
items={usuarios}
keyExtractor={(u) => u.id}
renderItem={(usuario) => (
<span>{usuario.nombre} — {usuario.rol}</span>
)}
/>Extender elementos HTML nativos
Un patrón muy útil es crear componentes que extiendan elementos HTML nativos:
// Extiende todos los atributos de un <button> + los nuestros
interface BotonPrimarioProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
cargando?: boolean;
}
function BotonPrimario({ cargando = false, children, disabled, ...resto }: BotonPrimarioProps) {
return (
<button
type="button"
className="btn-primario"
disabled={disabled || cargando}
aria-busy={cargando}
{...resto}
>
{cargando ? 'Cargando…' : children}
</button>
);
}
// Acepta onClick, aria-label, id, y cualquier atributo de button
<BotonPrimario onClick={enviar} aria-label="Guardar cambios" cargando={guardando}>
Guardar
</BotonPrimario>Prop drilling y sus límites
Cuando los datos necesitan pasar por múltiples niveles de componentes, el prop drilling se vuelve problemático:
App → Layout → Pagina → Seccion → Tarjeta → Avatar (usa el usuario)Si solo Avatar necesita el usuario, pasar la prop por todos los niveles intermedios es tedioso y frágil. La solución es el Context API (que veremos en la lección 8) o un gestor de estado global.
Regla práctica: si una prop pasa por más de 2-3 niveles sin ser usada, es momento de considerar Context o estado global.
En la próxima lección aprenderemos useState para agregar estado interno a los componentes y manejar eventos de usuario.
Inicia sesión para guardar tu progreso