En esta página

JSX y Componentes

15 min lectura TextoCap. 1 — Fundamentos de React

¿Qué es JSX?

JSX (JavaScript XML) es la extensión de sintaxis que usa React para describir la interfaz de usuario. Parece HTML dentro de JavaScript, pero es en realidad azúcar sintáctico que el compilador transforma en llamadas a funciones de React.

// Lo que escribes (JSX)
const elemento = <h1 className="titulo">¡Hola, mundo!</h1>;

// Lo que el compilador genera (JavaScript puro)
const elemento = React.createElement('h1', { className: 'titulo' }, '¡Hola, mundo!');

Con TypeScript y Vite, el compilador de JSX está configurado automáticamente. No necesitas importar React en cada archivo desde React 17+, aunque es buena práctica importar el tipo React.JSX.Element para las firmas de función.

Reglas fundamentales de JSX

1. Un solo elemento raíz

Cada componente debe retornar un único elemento raíz. Si necesitas múltiples elementos, envuélvelos en un fragmento:

// ❌ Error: múltiples elementos raíz
function Mal() {
  return (
    <h1>Título</h1>
    <p>Párrafo</p>
  );
}

// ✅ Correcto: fragmento
function Bien() {
  return (
    <>
      <h1>Título</h1>
      <p>Párrafo</p>
    </>
  );
}

2. Expresiones JavaScript entre llaves

Cualquier expresión JavaScript válida puede usarse dentro de {}. Esto incluye variables, llamadas a funciones, operadores ternarios y métodos de array:

function Info() {
  const usuario = 'Ana';
  const esPremium = true;
  const fecha = new Date().toLocaleDateString('es-ES');

  return (
    <div>
      <p>Usuario: {usuario.toUpperCase()}</p>
      <p>Plan: {esPremium ? 'Premium' : 'Gratuito'}</p>
      <p>Fecha: {fecha}</p>
      <p>Suma: {2 + 2}</p>
    </div>
  );
}

Nota: las sentencias (if, for, while) no son expresiones y no pueden ir directamente en JSX. Usa el operador ternario o lógico && en su lugar.

3. Atributos en camelCase

Los atributos HTML se escriben en camelCase en JSX, con algunas excepciones importantes:

HTML JSX
class className
for htmlFor
onclick onClick
tabindex tabIndex
stroke-width strokeWidth
<label htmlFor="email">Correo electrónico</label>
<input
  id="email"
  type="email"
  className="campo-texto"
  onChange={(e) => console.log(e.target.value)}
  tabIndex={0}
/>

4. El atributo style es un objeto

// ❌ String (como en HTML)
<div style="color: red; font-size: 16px">

// ✅ Objeto JavaScript
<div style={{ color: 'red', fontSize: '16px' }}>

Componentes funcionales con TypeScript

Un componente React es simplemente una función que retorna JSX. Con TypeScript, tipamos sus props con interfaces:

interface PerfilUsuarioProps {
  nombre: string;
  avatar: string;
  rol: 'admin' | 'editor' | 'lector';
  articulosPublicados: number;
}

function PerfilUsuario({
  nombre,
  avatar,
  rol,
  articulosPublicados,
}: PerfilUsuarioProps): React.JSX.Element {
  return (
    <div className="perfil">
      <img src={avatar} alt={`Avatar de ${nombre}`} />
      <h2>{nombre}</h2>
      <span className={`badge badge-${rol}`}>{rol}</span>
      <p>{articulosPublicados} artículos publicados</p>
    </div>
  );
}

La convención es usar PascalCase para los componentes (para distinguirlos de los elementos HTML nativos) y camelCase para las funciones y variables regulares.

Renderizado de listas

El método más común para renderizar listas es usar Array.prototype.map(). El atributo key es obligatorio y debe ser único entre los hermanos del mismo nivel:

interface Tarea {
  id: string;
  texto: string;
  completada: boolean;
}

function ListaTareas({ tareas }: { tareas: Tarea[] }): React.JSX.Element {
  return (
    <ul>
      {tareas.map((tarea) => (
        <li
          key={tarea.id}
          style={{ textDecoration: tarea.completada ? 'line-through' : 'none' }}
        >
          {tarea.texto}
        </li>
      ))}
    </ul>
  );
}

¿Por qué key es tan importante?

React usa key para identificar qué elementos cambiaron, se agregaron o eliminaron. Sin ella (o con keys incorrectas como el índice del array), React puede comportarse de manera inesperada al reordenar o filtrar la lista, causando bugs difíciles de detectar como campos de formulario que no se actualizan correctamente.

Renderizado condicional

Hay varias formas de mostrar u ocultar contenido condicionalmente:

Operador ternario

function Estado({ activo }: { activo: boolean }) {
  return (
    <span className={activo ? 'verde' : 'rojo'}>
      {activo ? 'En línea' : 'Desconectado'}
    </span>
  );
}

Operador lógico &&

Ideal para renderizar algo o nada:

function Notificacion({ mensaje }: { mensaje: string | null }) {
  return (
    <div>
      <h1>Dashboard</h1>
      {mensaje && <div className="alerta">{mensaje}</div>}
    </div>
  );
}

Cuidado con valores falsy: si mensaje pudiera ser 0 o '', el operador && podría renderizar 0 o nada inesperadamente. Usa comparación explícita:

{mensaje !== null && <div className="alerta">{mensaje}</div>}

Retorno anticipado

Para lógica más compleja, el retorno anticipado es más legible:

function Contenido({ cargando, error, datos }: ContenidoProps) {
  if (cargando) return <p>Cargando…</p>;
  if (error) return <p>Error: {error}</p>;
  if (!datos) return null;

  return <div>{datos.titulo}</div>;
}

Componentes como unidades de composición

La verdadera potencia de React está en la composición: construir componentes complejos ensamblando componentes simples.

function EncabezadoPagina(): React.JSX.Element {
  return (
    <header>
      <Logo />
      <NavegacionPrincipal />
      <BotonSesion />
    </header>
  );
}

Cada componente tiene una única responsabilidad y puede desarrollarse, probarse y reutilizarse de forma independiente. Esta es la base de la arquitectura React.

En la siguiente lección veremos cómo pasar datos entre componentes mediante props y el poderoso patrón de composición con children.

Nunca uses el índice como key en listas dinámicas
Usar el índice del array como key causa bugs sutiles cuando la lista se reordena, filtra o se insertan elementos. Siempre usa un ID único y estable del dato. Solo está bien usar el índice en listas estáticas que nunca cambian de orden.
JSX no es HTML — diferencias clave
En JSX usas className (no class), htmlFor (no for), camelCase para eventos (onClick, onChange) y style recibe un objeto JavaScript. Todo elemento debe cerrarse: <img /> <br /> <input />. Los comentarios van entre {/* */}.
Fragmentos con key en listas
Si necesitas renderizar múltiples elementos por ítem y también necesitas key, usa la forma larga <React.Fragment key={id}> en lugar del shorthand <>. El shorthand no acepta atributos.