Ir al contenido
wannacode

Props: datos desde arriba

En resumen: Los props son los datos que un componente recibe de su padre, igual que una función recibe parámetros; el hijo no los cambia, y children trae contenido desde fuera.

Problema

Tres veces pan

El catálogo tiene que mostrar tres productos: pan, leche y queso. La tarjeta de producto está lista de la lección anterior, así que App la pone tres veces.

React

Las tres tarjetas son pan. El producto está escrito dentro de ProductCard, y todas las tarjetas lo muestran. El otro camino sería BreadCard, MilkCard y CheeseCard: tres copias del mismo componente, y cada cambio en la tarjeta habría que hacerlo tres veces. Lo que falta es una forma de decirle a la tarjeta qué producto mostrar.

La idea

Props: datos desde arriba

Un componente es una función. Una función recibe sus datos como parámetros, y un componente los recibe como atributos de su padre: el componente que lo usa. Estos atributos se llaman props (de «properties»). El padre los escribe, el hijo los lee.

App.jsx
<ProductCard product={bread} />
<ProductCard product={milk} />
ProductCard.jsx
export default function ProductCard({ product }) {
return <h2>{product.name}</h2>;
}

React reúne todos los atributos en un objeto y se lo pasa a la función: { product: bread }. La notación ({ product }) saca de él la propiedad product (desestructuración). Sin las llaves el parámetro es el objeto entero: function ProductCard(props), y luego props.product.name.

Hay una sola tarjeta, y cada uso tiene datos distintos. Con una función pasa lo mismo: una definición, muchas llamadas con argumentos distintos.

Míralo

Mira cómo fluyen

const bread = { name: 'Pan blanco', price: 1.2 };
const milk = { name: 'Leche fresca', price: 1.6 };
const cheese = { name: 'Queso blanco', price: 7.9 };
function ProductCard({ product, discount = 0 }) {
return (
<article>
<h2>{product.name}</h2>
<p>Precio: {money(product.price * (1 - discount / 100))}</p>
</article>
);
}
export default function App() {
return (
<main>
<h1>Pan y leche</h1>
<div>
<ProductCard product={bread} />
<ProductCard product={milk} />
<ProductCard product={cheese} discount={10} />
</div>
</main>
);
}
Paso a paso, en palabras
  1. App guarda los datos: tres productos. Debajo hay tres ProductCard: el mismo componente tres veces, y ninguna sabe aún qué mostrar.
  2. product={bread} le pasa el objeto bread a la primera tarjeta. Los atributos de un componente se llaman props.
  3. Las otras dos tarjetas reciben su propio producto. La tercera recibe además discount = 10. Cada tarjeta tiene sus propios props.
  4. Cada tarjeta se dibuja a partir de sus props. El pan y la leche no recibieron discount, por eso vale 0 por defecto. El queso lleva un 10 % de descuento: 7,11 €.
  5. Si la tarjeta intenta props.discount = 0, no sube nada: en modo de desarrollo React lanza un TypeError (el objeto de props está congelado), y los datos de App siguen igual.
  6. Los datos fluyen en una sola dirección: del padre hacia abajo, al hijo. Para cambiar algo arriba, el hijo tiene que pedírselo al padre. Ese es el módulo «Estado».

Cada tarjeta conoce solo sus propios props. No ve las otras tarjetas, y no sabe de dónde salen los datos de App.

Míralo

Una tarjeta, tres productos

React

Los productos están en App, y hay una sola ProductCard. Añade un cuarto producto en App (la imagen img/apples.svg existe) y un <ProductCard … /> más. La tarjeta no la tocas. Con 30 productos querrás una lista en lugar de 30 líneas: es la próxima lección.

Pruébalo

Encuentra el error

La tarjeta no se muestra, y en la consola dice «TypeError: Cannot read properties of undefined (reading 'toFixed')».

Elige la línea con el error.

La idea

Un valor por defecto

Algunos productos están en oferta. La tarjeta recibe un segundo prop, discount, en porcentaje. La mayoría de los productos no tienen descuento, y no queda bien que cada línea de App escriba discount={0}. Un prop que falta es undefined, igual que un argumento que falta en una función. El precio se convierte en price * (1 - undefined / 100), o sea NaN.

Por eso el prop recibe un valor por defecto:

export default function ProductCard({ product, discount = 0 }) {

Es un valor por defecto de la desestructuración de JavaScript: si falta discount, vale 0. En código antiguo verás ProductCard.defaultProps = { discount: 0 } y propTypes. React 19 ya no los mira en los componentes de función: comprobar los tipos es trabajo de TypeScript.

Pruébalo

Un descuento

App le pasa al queso un discount de 10 (por ciento). Haz que la tarjeta acepte discount, 0 por defecto, y que muestre el precio con el descuento: el pan 1,20 €, la leche 1,60 €, el queso 7,11 €.

React

La idea

Solo lectura

Los datos son de quien los tiene. La tarjeta puede leer sus props, pero no puede cambiarlos. Prueba el botón «Uno más»: el hijo intenta aumentar su propio qty.

React

La cantidad se queda en 2, y la consola dice «TypeError: Cannot assign to read only property 'qty' of object». En modo de desarrollo React congela el objeto de props, justo para atrapar código como este.

Aunque pasara, el cambio sería solo en el hijo: App seguiría diciendo 2, y en el siguiente dibujo App volvería a pasar 2. Dos lugares con verdades distintas son el error de «Una lista a partir de datos». La regla: los datos fluyen hacia abajo, y cambiarlos es trabajo del padre. Cómo le pide un hijo a su padre que cambie algo es el módulo «Estado».

La idea

Contenido desde fuera

La tienda tiene marcos que se ven igual y llevan cosas distintas dentro: «Envío» con dos párrafos, «Devoluciones» con uno. El título es un prop. El contenido es un trozo entero de HTML, y es más cómodo escribirlo entre la etiqueta de apertura y la de cierre, como con <section>.

<Card title="Envío">
<p>En un máximo de 2 días laborables.</p>
</Card>
export default function Card({ title, children }) {
return (
<section className="card">
<h2>{title}</h2>
{children}
</section>
);
}

Todo lo que hay entre <Card> y </Card> llega en el prop especial children. Card decide dónde lo pone.

React

Pruébalo

El marco

Card muestra solo el título. Haz que muestre también, bajo el título, el contenido que App escribe entre <Card> y </Card>.

React
Más precisamente, para curiosos

Más de un lugar para contenido. Hay un solo children. Si el marco también tiene una parte de abajo, es un prop más con JSX: <Card title="Envío" footer={<a href="/delivery">Detalles</a>}>…</Card>, y Card pone {footer} donde corresponde.

Todas las propiedades de golpe. <ProductCard {...product} /> pasa cada propiedad del objeto como un prop aparte: name, price, image. Es cómodo, pero no se ve qué recibe exactamente la tarjeta.

Solo el primer nivel está congelado. props.qty = 3 lanza un error, pero props.product.price = 2 pasa en silencio: el objeto del producto no está congelado, es el mismo objeto que en App. Así el hijo cambia los datos del padre a sus espaldas. No lo hagas.

Tipos. React 19 ya no comprueba propTypes en los componentes de función y no usa defaultProps en ellos. Los tipos de los props se escriben con TypeScript: aparece en la lección sobre un proyecto real.

Ejercicio

El escaparate

Construye el escaparate desde cero. ProductCard recibe product y discount (0 por defecto) y muestra la imagen (alt es el nombre), el nombre como título de nivel dos y «Precio: …» con el descuento. Card recibe title y muestra el título (nivel dos) y el contenido desde fuera. En App, bajo el título: las tres tarjetas en un div (el queso con discount 10), después una Card «Envío» con «En un máximo de 2 días laborables.» y «Gratis a partir de 30 €.».

React

Test

Ponte a prueba

1. ¿Qué son los props?
2. ¿Qué recibe la tarjeta de <ProductCard product="bread" />?
3. La tarjeta recibe qty y quiere sumarle 1. ¿Qué es verdad?
4. App escribe <Card title="Envío"><p>En un máximo de 2 días laborables.</p></Card>. ¿Cómo muestra Card el párrafo?
5. App escribe <ProductCard product={bread} /> sin discount. ¿Qué es discount en la tarjeta?

Creado por Ivo Gergov como parte de la campaña de Evo Tech por una educación accesible en programación ·