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:
- Componentes reutilizables: construyes la UI como piezas independientes.
- Flujo de datos unidireccional: los datos fluyen de padres a hijos, lo que hace el comportamiento predecible.
- Ecosistema enorme: miles de bibliotecas compatibles, gran comunidad.
- React Native: el mismo conocimiento sirve para apps móviles.
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 useEffect | Cuá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
- React usa el Virtual DOM para actualizar eficientemente solo las partes del DOM que cambiaron, en lugar de reescribir toda la página.
- JSX permite escribir HTML dentro de JavaScript; usa
className, cierra todas las etiquetas, y las expresiones JS van entre llaves {}.
- Los componentes funcionales son funciones que retornan JSX; deben empezar con mayúscula y ser puros (mismo input = mismo output).
- Las props pasan datos de padre a hijo; son de solo lectura. Usa desestructuración y valores por defecto para limpiar el código.
- useState añade estado local a un componente; nunca modifiques el estado directamente — siempre usa la función setter.
- useEffect ejecuta efectos secundarios (fetch de datos, suscripciones) después del renderizado; el array de dependencias controla cuándo se ejecuta.
- Las listas en JSX se renderizan con
.map() y cada elemento debe tener una prop key única y estable.