Programación Web · Capítulo 11

Introducción a React: Componentes, Props y Estado

Aprende la biblioteca más popular para construir interfaces de usuario modernas, reactivas y reutilizables.


1. ¿Qué es React y por qué existe?

React es una biblioteca de JavaScript desarrollada por Facebook (Meta) en 2013 para construir interfaces de usuario. Surgió para resolver un problema concreto: cuando los datos de una aplicación cambian, actualizar el DOM manualmente es lento, propenso a errores y difícil de mantener.

Virtual DOM: React mantiene una representación en memoria del DOM real. Cuando el estado cambia, calcula la diferencia mínima entre el Virtual DOM anterior y el nuevo (proceso llamado reconciliation) y solo actualiza las partes del DOM real que cambiaron. Esto es mucho más eficiente que reemplazar todo el HTML.

Las ventajas clave de React son:

2. JSX: JavaScript + HTML

JSX (JavaScript XML) es una extensión de sintaxis que permite escribir HTML dentro de JavaScript. No es HTML real — el compilador (Babel/SWC) lo transforma en llamadas a React.createElement().

// JSX — lo que escribes const elemento = <h1 className="titulo">Hola, Mundo</h1>; // Lo que React ve por debajo const elemento = React.createElement("h1", { className: "titulo" }, "Hola, Mundo"); // Reglas importantes de JSX: // 1. Usar className en lugar de class // 2. Siempre cerrar las etiquetas: <img /> no <img> // 3. Un solo elemento raíz (o usar Fragments: <>...</>) // 4. Las expresiones JS van entre llaves {} const nombre = "Ana"; const bienvenida = ( <> <h1>Hola, {nombre}!</h1> <p>Son las {new Date().getHours()} horas.</p> </> );

3. Componentes Funcionales

Un componente en React es una función que recibe props y retorna JSX. Los componentes deben empezar con mayúscula para distinguirlos de las etiquetas HTML.

// Componente más simple posible function Saludo() { return <h1>Hola desde un componente!</h1>; } // Componente con lógica function TarjetaUsuario() { const usuario = { nombre: "Carlos López", profesion: "Desarrollador", activo: true }; return ( <div style={{ border: "1px solid #ccc", padding: "16px", borderRadius: "8px" }}> <h2>{usuario.nombre}</h2> <p>{usuario.profesion}</p> <span>{usuario.activo ? "✅ Activo" : "❌ Inactivo"}</span> </div> ); } // Usar el componente en la aplicación function App() { return ( <div> <Saludo /> <TarjetaUsuario /> </div> ); }

4. Props: Pasando Datos a los Componentes

Las props (propiedades) son el mecanismo para pasar datos de un componente padre a un componente hijo. Son de solo lectura — un componente nunca debe modificar sus propias props.

// Componente que recibe props function TarjetaProducto({ nombre, precio, disponible, imagen }) { return ( <div className="tarjeta"> <img src={imagen} alt={nombre} /> <h3>{nombre}</h3> <p>Precio: ${precio}</p> <button disabled={!disponible}> {disponible ? "Agregar al carrito" : "Sin stock"} </button> </div> ); } // Props con valores por defecto function Boton({ texto = "Clic aquí", color = "blue", onClick }) { return ( <button style={{ backgroundColor: color, color: "white", padding: "8px 16px" }} onClick={onClick} > {texto} </button> ); } // Usando los componentes con diferentes props function Tienda() { return ( <div> <TarjetaProducto nombre="Laptop Pro" precio={999} disponible={true} imagen="/laptop.jpg" /> <TarjetaProducto nombre="Mouse Inalámbrico" precio={45} disponible={false} imagen="/mouse.jpg" /> <Boton texto="Ver más productos" color="#0d9488" /> </div> ); }

5. useState: Estado en los Componentes

Estado es cualquier dato que puede cambiar con el tiempo y que cuando cambia, provoca que el componente se vuelva a renderizar. El hook useState añade estado a los componentes funcionales.

Contador Simple

import { useState } from "react"; function Contador() { // useState retorna [valorActual, funcionParaActualizar] const [conteo, setConteo] = useState(0); return ( <div> <h2>Conteo: {conteo}</h2> <button onClick={() => setConteo(conteo + 1)}>Incrementar</button> <button onClick={() => setConteo(conteo - 1)}>Decrementar</button> <button onClick={() => setConteo(0)}>Resetear</button> </div> ); }

Formulario con Estado

import { useState } from "react"; function FormularioRegistro() { const [formulario, setFormulario] = useState({ nombre: "", email: "", contrasena: "" }); const [enviado, setEnviado] = useState(false); // Manejador genérico para todos los campos function manejarCambio(e) { const { name, value } = e.target; setFormulario(prev => ({ ...prev, [name]: value })); } function manejarEnvio(e) { e.preventDefault(); // Prevenir recarga de página console.log("Datos enviados:", formulario); setEnviado(true); } if (enviado) { return <p>¡Registro exitoso, {formulario.nombre}!</p>; } return ( <form onSubmit={manejarEnvio}> <input name="nombre" placeholder="Tu nombre" value={formulario.nombre} onChange={manejarCambio} /> <input name="email" type="email" placeholder="tu@email.com" value={formulario.email} onChange={manejarCambio} /> <input name="contrasena" type="password" placeholder="Contraseña" value={formulario.contrasena} onChange={manejarCambio} /> <button type="submit">Registrarse</button> </form> ); }

6. useEffect: Efectos Secundarios

useEffect ejecuta código después de que el componente se renderiza. Sirve para cargar datos, suscribirse a eventos, actualizar el título de la página, y más.

import { useState, useEffect } from "react"; function ListaUsuarios() { const [usuarios, setUsuarios] = useState([]); const [cargando, setCargando] = useState(true); const [error, setError] = useState(null); // Se ejecuta después del primer renderizado useEffect(() => { async function cargarUsuarios() { try { const res = await fetch("https://jsonplaceholder.typicode.com/users"); const datos = await res.json(); setUsuarios(datos); } catch (err) { setError("No se pudieron cargar los usuarios"); } finally { setCargando(false); } } cargarUsuarios(); }, []); // [] = solo se ejecuta una vez al montar if (cargando) return <p>Cargando...</p>; if (error) return <p style={{ color: "red" }}>{error}</p>; return ( <ul> {usuarios.map(u => ( <li key={u.id}>{u.name} — {u.email}</li> ))} </ul> ); }
Dependencias de useEffectCuándo se ejecuta
useEffect(() => {...})Después de cada renderizado
useEffect(() => {...}, [])Solo al montar el componente
useEffect(() => {...}, [valor])Cuando valor cambia

7. Renderizado Condicional y Listas

function PanelUsuario({ usuario, esAdmin }) { // Renderizado condicional con && return ( <div> <h2>Bienvenido, {usuario.nombre}</h2> {esAdmin && <button>Panel de Administración</button>} {usuario.activo ? <span>✅ Activo</span> : <span>❌ Inactivo</span>} </div> ); } // Listas con .map() — cada elemento DEBE tener key única function ListaTareas({ tareas }) { if (tareas.length === 0) { return <p>No hay tareas pendientes.</p>; } return ( <ul> {tareas.map(tarea => ( <li key={tarea.id} style={{ textDecoration: tarea.completada ? "line-through" : "none" }}> {tarea.texto} </li> ))} </ul> ); }

8. Estructura de un Proyecto React

mi-proyecto/ ├── src/ │ ├── components/ # Componentes reutilizables │ │ ├── Boton.jsx │ │ ├── TarjetaUsuario.jsx │ │ └── Navbar.jsx │ ├── pages/ # Páginas completas │ │ ├── Inicio.jsx │ │ └── Perfil.jsx │ ├── hooks/ # Custom hooks │ │ └── useDatos.js │ ├── context/ # Estado global │ │ └── AuthContext.jsx │ ├── App.jsx # Componente raíz │ └── main.jsx # Punto de entrada ├── public/ │ └── index.html └── package.json

Resumen del Capítulo