En el capítulo anterior viste que un componente es una función que devuelve JSX, y que ese JSX se parece a HTML. Aquí abrimos esa caja: qué es exactamente el JSX, cómo meter datos dentro, cómo combinar componentes entre sí, y qué hace React por debajo para que todo esto sea rápido.
JSX: HTML que en realidad es JavaScript#
JSX es la sintaxis con la que describes la interfaz dentro de un componente. Se parece al HTML del Nivel 1 a propósito, pero no es HTML: es JavaScript disfrazado. Antes de llegar al navegador, una herramienta lo transforma en llamadas normales de JS que crean los elementos.
// Esto que escribes...
const saludo = <h1>Team Builder</h1>;
// ...se transforma, por debajo, en algo así (una llamada que crea el elemento):
// const saludo = jsx("h1", { children: "Team Builder" });No tienes que escribir esa segunda forma nunca: la herramienta de build lo hace por ti. Pero saber que el JSX acaba siendo JavaScript explica casi todas sus reglas, como verás enseguida.
Meter valores con llaves {}#
Dentro del JSX, unas llaves {} abren una ventana a JavaScript: lo que pongas dentro se
evalúa y su resultado aparece en la interfaz. Esto es lo que hace que la pantalla dependa de
tus datos.
// Una variable dentro del JSX:
const nombre = "Tracer";
// {nombre} evalúa la variable y coloca su valor en el texto.
const titulo = <h2>{nombre}</h2>;No solo variables: dentro de {} cabe cualquier expresión (cualquier trozo de JS que
devuelva un valor).
// Una operación: se calcula y se muestra el resultado.
const victorias = 78;
const partidas = 120;
// {} con una cuenta dentro: React muestra 65.
const winrate = <span>{(victorias / partidas) * 100}</span>;// Un ternario (Nivel 2): elige qué texto mostrar según una condición.
const activo = true;
// Si activo es true muestra "En forma"; si no, "En reserva".
const estado = <span>{activo ? "En forma" : "En reserva"}</span>;La palabra clave es expresión: algo que devuelve un valor. Por eso dentro de {} no
caben sentencias como if o for (esas no devuelven nada). Para decidir, usas el ternario;
para listas, lo verás en el capítulo de listas.
Atributos en JSX#
Los atributos se parecen a los de HTML, pero con dos diferencias que vienen de que el JSX es JavaScript.
La primera: class es una palabra reservada de JavaScript (sirve para crear clases, Nivel 2),
así que en JSX el atributo se llama className. Si por costumbre escribes class, React te
lo avisa en la consola del navegador (te dice que seguramente querías className): no rompe la
app, pero es la señal de que el atributo correcto es className y de que tus estilos quizá no
se están aplicando como crees.
// En HTML sería class="tarjeta"; en JSX es className.
const caja = <div className="tarjeta">Contenido</div>;Por la misma razón, el atributo for de las etiquetas <label> se escribe htmlFor:
// En HTML sería <label for="email">; en JSX es htmlFor.
const etiqueta = <label htmlFor="email">Email</label>;Los estilos en línea no son un texto, sino un objeto de JavaScript, y por eso van con
dobles llaves: las de fuera son la ventana a JS, las de dentro son el objeto. Las propiedades
van en camelCase (backgroundColor, no background-color).
// {{ }} = una ventana a JS ({...}) que contiene un objeto ({...}) de estilos.
const aviso = <p style={{ color: "tomato", fontWeight: 600 }}>Atención</p>;En la práctica preferirás className y una hoja de estilos (como en el ejercicio) antes que
estilos en línea, pero conviene saber leerlos.
Por último, en JSX toda etiqueta tiene que cerrarse. Las que en HTML podían ir solas
(<img>, <br>, <input>) se cierran con />:
// Etiqueta sin contenido: se autocierra con />.
const logo = <img src="/tracer.png" alt="Tracer" />;Un solo nodo raíz (y el Fragment)#
Un componente, como una función, devuelve una sola cosa. En JSX eso significa un único nodo raíz: no puedes devolver dos elementos sueltos uno al lado del otro.
// ESTO NO COMPILA: dos elementos sueltos, sin un padre que los envuelva.
function Mal() {
return (
<h2>Tracer</h2>
<p>Daño</p>
);
}Una opción es envolverlos en un <div>, pero eso mete en el DOM un nodo que quizá no quieres.
Para esos casos React trae el Fragment: un envoltorio invisible, <>...</>, que agrupa sin
añadir nada al DOM.
// El Fragment <>...</> agrupa los dos elementos sin crear un <div> de más.
function Bien() {
return (
<>
<h2>Tracer</h2>
<p>Daño</p>
</>
);
}Comentarios dentro del JSX#
Dentro del JSX, un // normal no vale (rompería el marcado). Los comentarios van en una
expresión {}, con la forma {/* ... */}:
// Comentario JSX: va entre llaves, con la forma {/* ... */}.
const tarjeta = (
<article>
{/* El nombre del héroe */}
<h2>Tracer</h2>
</article>
);Componer componentes#
Aquí está la idea grande de React. Un componente puede usar a otros, como si fueran etiquetas nuevas. Construyes pantallas combinando piezas pequeñas, cada una con su responsabilidad. A eso se le llama composición.
// Una pieza pequeña: la cabecera.
function Header() {
return <h1>Team Builder</h1>;
}
// Otra pieza: la tarjeta de un héroe.
function HeroCard() {
return <article>Tracer — Daño</article>;
}
// App COMPONE la interfaz usando las otras como etiquetas: <Header /> y <HeroCard />.
function App() {
return (
<>
<Header />
<HeroCard />
</>
);
}Fíjate en la mayúscula: <Header /> (tuyo) frente a <article> (del navegador). React usa esa
mayúscula inicial para distinguir tus componentes de las etiquetas HTML. Por ahora cada pieza
lleva sus datos fijos dentro; en el próximo capítulo aprenderás a pasarles datos con props,
y ahí los componentes se vuelven de verdad reutilizables.
Qué hace React por dentro: el Virtual DOM#
En el Nivel 2 tocabas el DOM a mano, y tocar el DOM es caro: cada cambio puede obligar al navegador a recalcular y repintar. React evita ese coste con el Virtual DOM: una copia ligera del DOM que mantiene en memoria.
Cuando algo cambia, React no repinta toda la página. Genera una versión nueva del Virtual DOM, la compara con la anterior para ver qué ha cambiado de verdad —ese proceso se llama reconciliación— y aplica al DOM real solo las diferencias. Tú describes el qué (con JSX); React calcula el cómo mínimo.
Cuidado con un parecido que confunde: el Shadow DOM no es esto. El Shadow DOM es una función del navegador para los web components, que encapsula marcado y estilos para que no se mezclen con el resto de la página. Suena parecido, pero no tiene nada que ver con el Virtual DOM: uno es una técnica interna de React para actualizar rápido; el otro, un mecanismo de aislamiento del navegador.
Pruébalo#
La tarjeta del Team Builder, ya en componentes: App compone Header y HeroCard, con
expresiones {} para el winrate. Cambia un dato o un texto y pulsa Ejecutar.
Comprueba lo que sabes#
Pregunta 1 de 4
¿Qué es JSX?
Tu turno#
Ejercicio · en esta página
La tarjeta de héroe, en componentes
Crea un componente HeroCard con el nombre y el rol de un héroe (datos fijos), usando className, y compón la interfaz desde App. El styles.css ya está hecho.
Paso 1: Compone y renderiza
- HeroCard es una función que empieza por mayúscula y devuelve JSX
- Muestra el nombre y el rol del héroe, con className en el contenedor
- App usa <HeroCard /> (composición) y se ve la tarjeta con estilo
Paso 2: Datos derivados y JSX expresivo
- El winrate se calcula y se inserta con una expresión {}
- Un comentario JSX {/* */} documenta alguna parte
- Un solo nodo raíz y todo comentado con su porqué
Paso 3: Composición limpia y responsive
- La interfaz se compone de piezas pequeñas (Header + HeroCard) en un Fragment
- HTML semántico (article, encabezados) y datos derivados
- Aguanta a 375px sin romperse
Ver soluciones
// HeroCard: un componente con la tarjeta de un héroe.
// Sus datos van fijos en el código; pasárselos desde fuera es el próximo capítulo (props).
function HeroCard() {
// Un componente devuelve UN SOLO nodo raíz: aquí, el <article> de la tarjeta.
return (
<article className="tarjeta">
{/* El nombre del héroe */}
<h2 className="tarjeta__nombre">Tracer</h2>
{/* Su rol dentro del equipo */}
<p className="tarjeta__rol">Daño</p>
</article>
);
}
// App se exporta por defecto para que el motor lo monte.
export default function App() {
// App usa HeroCard como si fuera una etiqueta más: eso es composición.
return <HeroCard />;
} Por qué este nivel
- Lo esencial: un componente HeroCard que devuelve un solo nodo raíz y un App que lo compone. La interfaz ya no es una sola pieza: son componentes que se combinan.
// HeroCard: la tarjeta de un héroe. Datos fijos por ahora (las props llegan en el próximo capítulo).
function HeroCard() {
// Datos del héroe.
const nombre = "Tracer";
const rol = "Daño";
const partidas = 120;
const victorias = 78;
// Dato derivado: el winrate se calcula, no se escribe a mano.
const winrate = (victorias / partidas) * 100;
// Un solo nodo raíz. Dentro, {} mete valores de JavaScript en el JSX.
return (
<article className="tarjeta">
{/* {nombre} evalúa la variable y coloca su valor aquí */}
<h2 className="tarjeta__nombre">{nombre}</h2>
<p className="tarjeta__rol">{rol}</p>
{/* toFixed(0) redondea el winrate; el "% de victorias" es texto normal */}
<p className="tarjeta__winrate">{winrate.toFixed(0)}% de victorias</p>
</article>
);
}
// App se exporta por defecto: el motor monta este componente.
export default function App() {
// Composición: App usa HeroCard.
return <HeroCard />;
} Por qué es mejor que el anterior
- El JSX deja de ser texto fijo: con {} inserta valores de JavaScript, y el winrate se CALCULA en vez de teclearse. Así la interfaz depende de los datos, no al revés.
- El comentario JSX {/* */} documenta dentro del marcado sin romperlo (un // normal ahí daría error).
// Header: la cabecera de la app. Un componente con una sola responsabilidad.
function Header() {
// Devuelve un único elemento, así que no necesita Fragment.
return <h1 className="titulo">Team Builder</h1>;
}
// HeroCard: la tarjeta de un héroe. Datos fijos por ahora (las props son el próximo capítulo).
function HeroCard() {
const nombre = "Tracer";
const rol = "Daño";
const partidas = 120;
const victorias = 78;
// El winrate es un dato derivado: se calcula a partir de partidas y victorias.
const winrate = (victorias / partidas) * 100;
// HTML semántico: <article> para una pieza de contenido con entidad propia.
return (
<article className="tarjeta">
<h2 className="tarjeta__nombre">{nombre}</h2>
<p className="tarjeta__rol">{rol}</p>
<p className="tarjeta__winrate">{winrate.toFixed(0)}% de victorias</p>
</article>
);
}
// App compone la interfaz a partir de piezas pequeñas (Header + HeroCard).
// Devuelve las dos dentro de un Fragment <>...</> para no meter un <div> de más en el DOM.
export default function App() {
return (
<>
<Header />
<HeroCard />
</>
);
} Por qué es mejor que el anterior
- La interfaz se parte en piezas pequeñas con una sola responsabilidad cada una (Header, HeroCard), y App las compone. Eso es lo que hace React mantenible: armas pantallas combinando componentes.
- Devolver dos elementos exige un único nodo raíz; el Fragment <>...</> lo resuelve sin ensuciar el DOM con un <div> de relleno.