React desde cero con Create React App: useState y useEffect
Serie: Desarrollo de Interfaces
Curso: Desarrollo de Interfaces 2
Unidad 1: Fundamentos de interfaces con React
Capítulo 01: React desde cero con Create React App: useState y useEffect
Nivel: Básico
Prerrequisitos: JavaScript, funciones, arreglos, objetos, HTML, CSS y operaciones asíncronas.
Tecnologías: React, Create React App, JavaScript, JSX, CSS y Fetch API.
Una interfaz deja de ser una maqueta cuando puede recordar una decisión, responder a una acción y comunicarse con información externa. React nos ayuda a construir ese tipo de experiencias mediante componentes que describen el resultado esperado.
En este capítulo construiremos esa idea desde dos Hooks esenciales. useState le da memoria a un componente y useEffect lo sincroniza con sistemas que están fuera de React, como una API o un temporizador. Los ejemplos se ejecutarán en una aplicación web creada con create-react-app, tal como requiere el curso. Si ya practicaste JavaScript aplicado a interfaces y APIs con Fetch, reconocerás varios fundamentos.
Conviene empezar con una precisión profesional: Create React App fue deprecado oficialmente en 2025 y ya no es la recomendación para iniciar productos nuevos. Aun así, permanece en modo de mantenimiento y resulta útil para comprender la estructura clásica de un proyecto React. Aprenderemos los conceptos con esta herramienta, pero también sabremos reconocer cuándo corresponde migrar a una alternativa vigente.
Qué aprenderás hoy
- Comprender qué aporta React al desarrollo de interfaces web.
- Distinguir componentes, propiedades, estado y renderizado.
- Crear y ejecutar un proyecto con Create React App.
- Usar
useStatepara recordar datos que cambian. - Usar
useEffectpara sincronizar un componente con una API o un temporizador. - Representar carga, éxito, error y resultados vacíos.
Una interfaz reactiva sigue un ciclo sencillo: renderiza, recibe una interacción, actualiza su estado y vuelve a renderizar. No modificamos cada elemento del DOM a mano; describimos cómo debe verse la interfaz para cada estado.
React en una frase
React es una biblioteca de JavaScript para construir interfaces a partir de componentes. Un componente es una función que puede recibir propiedades, conservar estado y devolver JSX. JSX se parece a HTML, pero vive dentro de JavaScript y permite combinar estructura, datos y comportamiento de forma declarativa.
En una aplicación React web seguimos utilizando elementos del navegador como main, h1, button, input o ul. Esta es una diferencia importante frente a React Native: aquí el resultado se renderiza en el DOM y se estiliza con CSS. Los Hooks pertenecen a React y pueden aparecer en ambas plataformas, pero los componentes y el entorno de ejecución no son los mismos.
Preparar el proyecto con Create React App
Instala una versión LTS de Node.js, abre una terminal y ejecuta los siguientes comandos. npx descarga y ejecuta el generador sin instalarlo globalmente:
npx create-react-app mi-primera-interfaz
cd mi-primera-interfaz
npm start
El último comando inicia el servidor de desarrollo y normalmente abre http://localhost:3000. La carpeta src concentra el código que modificaremos. Para comenzar, podemos reemplazar el contenido de src/App.js y trabajar los estilos en src/App.css.
Create React App muestra una advertencia de deprecación porque ya no recibe nuevas funcionalidades. Úsalo para esta práctica y para mantener proyectos existentes. Si luego inicias una aplicación profesional, revisa las recomendaciones actuales de React antes de elegir la herramienta de construcción.
useState: la memoria de un componente
Una variable común se crea otra vez cada vez que el componente se ejecuta. Además, cambiarla no le avisa a React que debe renderizar de nuevo. useState resuelve ambos problemas. Devuelve el valor actual y una función que permite solicitar su actualización:
const [cantidad, setCantidad] = useState(1);
Pensemos en una interfaz para reservar entradas. La cantidad cambia al presionar los botones y el total se calcula a partir de ella:
import { useState } from 'react';
import './App.css';
export default function App() {
const [cantidad, setCantidad] = useState(1);
const precio = 25;
const total = cantidad * precio;
function aumentar() {
setCantidad(actual => Math.min(actual + 1, 6));
}
function disminuir() {
setCantidad(actual => Math.max(actual - 1, 1));
}
return (
<main className="reserva">
<h1>Reserva de entradas</h1>
<p>Entradas: {cantidad}</p>
<p>Total: S/ {total}</p>
<button onClick={disminuir} disabled={cantidad === 1}>
Quitar
</button>
<button onClick={aumentar} disabled={cantidad === 6}>
Agregar
</button>
</main>
);
}
La forma funcional setCantidad(actual => ...) es recomendable cuando el siguiente valor depende del anterior. React puede agrupar actualizaciones y el argumento recibido representa el valor más reciente. Observa además que total no necesita otro estado: es un dato derivado que puede calcularse durante cada renderizado.
useEffect: sincronizarse con el exterior
useEffect sirve para conectar el componente con algo que React no controla: una solicitud de red, un temporizador, una suscripción, una API del navegador o una biblioteca externa. Recibe una función de configuración y una lista de dependencias. Cuando el proceso necesita detenerse, la función puede devolver una limpieza.
import { useEffect, useState } from 'react';
function Reloj() {
const [hora, setHora] = useState(new Date());
useEffect(() => {
const id = setInterval(() => setHora(new Date()), 1000);
return () => clearInterval(id);
}, []);
return <time>{hora.toLocaleTimeString()}</time>;
}
El arreglo vacío indica que el Effect no depende de valores reactivos del componente. Sin ese arreglo se ejecutaría después de cada renderizado; con [categoria], volvería a sincronizarse cuando cambiara categoria. La limpieza evita que el intervalo continúe activo cuando el componente desaparece.
Si puedes calcular un valor a partir de propiedades o estado, hazlo durante el renderizado. Reserva useEffect para sincronizaciones externas. Copiar un estado en otro mediante un Effect suele agregar renderizados y posibilidades de inconsistencia.
Ejemplo real: cargar publicaciones y permitir reintentos
Una interfaz conectada a datos debe considerar más que el caso exitoso. La red puede tardar, fallar o devolver una colección vacía. El siguiente componente consulta una API de práctica y usa estados para los datos, la situación de la solicitud y el número de intento. El Effect se ejecuta al montar el componente y cada vez que la persona solicita reintentar.
import { useEffect, useState } from 'react';
export default function App() {
const [publicaciones, setPublicaciones] = useState([]);
const [estado, setEstado] = useState('cargando');
const [intento, setIntento] = useState(0);
useEffect(() => {
const controlador = new AbortController();
async function cargarPublicaciones() {
setEstado('cargando');
try {
const respuesta = await fetch(
'https://jsonplaceholder.typicode.com/posts?_limit=5',
{ signal: controlador.signal }
);
if (!respuesta.ok) {
throw new Error('La respuesta no fue correcta');
}
const datos = await respuesta.json();
setPublicaciones(datos);
setEstado('listo');
} catch (error) {
if (error.name !== 'AbortError') {
setEstado('error');
}
}
}
cargarPublicaciones();
return () => controlador.abort();
}, [intento]);
if (estado === 'cargando') {
return <p role="status">Cargando publicaciones...</p>;
}
if (estado === 'error') {
return (
<div role="alert">
<p>No pudimos cargar las publicaciones.</p>
<button onClick={() => setIntento(actual => actual + 1)}>
Reintentar
</button>
</div>
);
}
return (
<main>
<h1>Publicaciones recientes</h1>
{publicaciones.length === 0 ? (
<p>No hay publicaciones disponibles.</p>
) : (
<ul>
{publicaciones.map(publicacion => (
<li key={publicacion.id}>{publicacion.title}</li>
))}
</ul>
)}
</main>
);
}
AbortController cancela la petición si el componente se desmonta o si el Effect necesita reiniciarse. La interfaz informa cada estado con texto visible y roles semánticos; no obliga al usuario a adivinar si la aplicación está trabajando o quedó bloqueada.
Dónde estos Hooks se vuelven servicio y negocio
- Comercio electrónico:
useStaterecuerda cantidades y filtros;useEffectconsulta inventario o sincroniza un carrito remoto. - Educación: el estado conserva una respuesta seleccionada y un Effect carga actividades o registra progreso autorizado.
- Reservas: el estado controla fecha y horario; un Effect consulta disponibilidad cuando cambia la sede.
- Paneles internos: el estado representa los filtros activos y un Effect recupera la información correspondiente.
La oportunidad no consiste en usar Hooks porque sean populares, sino en convertir datos y procesos en experiencias comprensibles. Para una marca o un emprendimiento, eso significa dar retroalimentación inmediata, mostrar información oportuna y no ocultar los fallos detrás de una pantalla vacía.
Errores comunes al comenzar
- Llamar Hooks dentro de condicionales, bucles o funciones anidadas. Deben estar en el nivel superior del componente o de un Hook propio.
- Modificar directamente un arreglo u objeto guardado en el estado. Crea un nuevo valor para que React pueda reconocer el cambio.
- Escribir
onClick={aumentar()}y ejecutar la función durante el renderizado. Debe pasarse la referencia:onClick={aumentar}. - Declarar
asyncla función entregada directamente auseEffect. Define la función asíncrona dentro y ejecútala. - Omitir dependencias para impedir que un Effect se ejecute. Eso puede dejarlo trabajando con valores antiguos.
- Probar solamente el camino feliz y olvidar carga, error, vacío, desconexión y accesibilidad.
En desarrollo, el modo estricto de React puede ejecutar un ciclo adicional de configuración y limpieza para detectar Effects frágiles. No es un error. Si la limpieza refleja correctamente la configuración, este ensayo adicional no debería alterar la experiencia.
Buenas prácticas para avanzar
- Mantener cada estado pequeño, claro y cercano al componente que lo necesita.
- Nombrar las variables por su significado:
estaCargando,productosoerror. - Derivar valores durante el renderizado cuando no exista una sincronización externa.
- Escribir cada Effect como un proceso independiente con inicio y, cuando corresponda, limpieza.
- Utilizar HTML semántico y comprobar la interfaz con teclado, no solo con el mouse.
- Separar componentes cuando
App.jsacumule demasiadas responsabilidades.
Reto práctico
Crea una interfaz web que cargue actividades desde una API o archivo JSON. Debe mostrar carga, error, lista vacía y resultados. Permite marcar actividades como favoritas con useState y agrega un botón de reintento que vuelva a activar el useEffect. Como variante profesional, incluye un filtro por categoría y muestra la hora de la última actualización.
Antes de programar, dibuja los cuatro estados de la interfaz. Luego explica qué datos pertenecen al estado, qué valores pueden derivarse y qué parte representa una sincronización externa. Si puedes justificar esas decisiones, ya estás pensando como desarrollador de interfaces y no como alguien que solamente copia Hooks.
Conclusión
React cambia nuestra relación con la interfaz: en lugar de ordenar modificaciones aisladas al DOM, describimos el resultado que corresponde a cada estado. useState permite que el componente recuerde y useEffect permite que se mantenga sincronizado con el exterior. Juntos resuelven muchos de los primeros desafíos de una aplicación web interactiva.
Create React App nos ofrece un entorno conocido para practicar estos fundamentos, aunque su etapa de mantenimiento nos recuerda una lección más amplia: las herramientas cambian y los modelos mentales permanecen. Tu meta no es memorizar un generador, sino construir una interfaz pequeña que responda, comunique y falle con honestidad.
Próximo capítulo
En el siguiente capítulo organizaremos la interfaz con componentes reutilizables, propiedades y estilos CSS.
Referencias consultadas
- Create React App: primeros pasos.
- React: deprecación de Create React App.
- React: iniciar un nuevo proyecto.
- React: describir la interfaz.
- React: referencia de useState.
- React: referencia de useEffect.
- React: cuándo probablemente no necesitas un Effect.
- MDN: AbortController.
- JSONPlaceholder: guía de uso.