Aprende a gestionar el estado de tus componentes con useState y crear interfaces interactivas
midudev
Más clases y directos en YouTube
Estado con useState
En la clase anterior aprendimos a manejar eventos, pero los componentes no recordaban nada. Cuando hacías clic en “Aplicar”, se ejecutaba la función pero el botón no cambiaba visualmente. Ahora vamos a solucionar eso con el estado.
¿Qué es el estado?
El estado es la memoria de un componente. Es donde guardas información que puede cambiar con el tiempo.
Analogía: El interruptor de luz
Piénsalo así. Imagina un interruptor de luz:
- Tiene un estado: encendido o apagado
- Cuando lo presionas, cambia de estado
- Y la luz se actualiza automáticamente
Lo mismo pasa con los componentes de React:
- Tienen un estado (por ejemplo:
aplicado = false) - Cuando el usuario interactúa, cambias el estado (a
aplicado = true) - React actualiza la interfaz automáticamente
¿Por qué necesitamos estado?
Hasta ahora, nuestros componentes son estáticos:
function Boton() { return <button>Haz clic</button>}
Este botón siempre dice “Haz clic”. No puede recordar si ya hiciste clic.
Con estado, podemos hacer componentes dinámicos que recuerdan cosas:
function Boton() { const [clicks, setClicks] = React.useState(0) return <button onClick={() => setClicks(clicks + 1)}>Clicks: {clicks}</button>}
Ahora el botón recuerda cuántas veces has hecho clic.
Gestionando estado con useState
Vamos a añadir estado a nuestro componente JobCard para que el botón “Aplicar” cambie cuando lo clickeas.
Actualiza tu archivo react.html:
function JobCard({ titulo, empresa, ubicacion, descripcion }) { // ¡NOVEDAD! Estado: ¿el usuario aplicó a este empleo? const [aplicado, setAplicado] = React.useState(false) const handleAplicar = () => { setAplicado(true) // Cambiamos el estado a true } return ( <article className="job-listing-card"> <div> <h3>{titulo}</h3> <small> {empresa} | {ubicacion} </small> <p>{descripcion}</p> </div> <button className={aplicado ? 'button-apply-job is-applied' : 'button-apply-job'} onClick={handleAplicar} disabled={aplicado} > {aplicado ? '¡Aplicado!' : 'Aplicar'} </button> </article> )}
¿Qué cambió?
Analicemos la novedad paso a paso.
useState: El Hook de estado
function JobCard({ titulo, empresa, ubicacion, descripcion }) { // Declarar estado const [aplicado, setAplicado] = React.useState(false) // ↓ ↓ ↓ // valor función valor inicial // actual para cambiar const handleAplicar = () => { setAplicado(true) // Actualizamos el estado } return ( <article className="job-listing-card"> {/* ... */} <button className={aplicado ? 'button-apply-job is-applied' : 'button-apply-job'} onClick={handleAplicar} disabled={aplicado} > {aplicado ? '¡Aplicado!' : 'Aplicar'} </button> </article> )}
Desglose de useState
const [aplicado, setAplicado] = React.useState(false)
¿Qué significa esto?
React.useState(false)- Crea una variable de estado con valor inicialfalseaplicado- Variable que contiene el valor actual del estadosetAplicado- Función para actualizar el estado
¿Por qué usar array destructuring?
// useState devuelve un array con 2 elementos:const resultado = React.useState(false)const aplicado = resultado[0] // El valorconst setAplicado = resultado[1] // La función// Destructuring hace esto más simple:const [aplicado, setAplicado] = React.useState(false)
Cómo funciona el flujo
- Estado inicial:
aplicado = false - Usuario hace clic en el botón
- Se ejecuta
handleAplicar - Se llama
setAplicado(true) - React actualiza
aplicadoatrue - React re-renderiza el componente
- El botón muestra “¡Aplicado!”, cambia de estilo y se deshabilita
Todo esto automáticamente ✨
Usando el estado en el JSX
<button className={aplicado ? 'button-apply-job is-applied' : 'button-apply-job'} disabled={aplicado}> {aplicado ? '¡Aplicado!' : 'Aplicar'}</button>
Tres cosas cambian según el estado:
className- Siaplicadoestrue, añade la claseis-applieddisabled- Deshabilita el botón cuandoaplicadoestrue- Texto - Cambia de “Aplicar” a “¡Aplicado!”
Nota sobre accesibilidad: Cuando cambias el estado de un botón dinámicamente (como cambiar el texto de “Aplicar” a “¡Aplicado!”), React actualiza el DOM y los lectores de pantalla generalmente detectan estos cambios automáticamente. El atributo
disabledtambién se anuncia correctamente. Si necesitas anunciar cambios más complejos o importantes, considera usararia-liveregions oaria-labelpara proporcionar contexto adicional sobre el cambio de estado.
Operador ternario
{ aplicado ? '¡Aplicado!' : 'Aplicar'}
Esto es un operador ternario:
condición ? siVerdadero : siFalso
Es como un if/else en una línea:
// Con if/else (no funciona en JSX directamente)if (aplicado) { return '¡Aplicado!'} else { return 'Aplicar'}// Con operador ternario (funciona en JSX)aplicado ? '¡Aplicado!' : 'Aplicar'
Comparación completa: Antes vs Ahora
Veamos todo el cambio de JavaScript vanilla a React con useState.
Antes (JavaScript vanilla)
En apply-button.js:
// Seleccionar elementoconst jobsListingSection = document.querySelector('.jobs-listings')// Añadir eventojobsListingSection.addEventListener('click', function (event) { const element = event.target if (element.classList.contains('button-apply-job')) { // Actualizar manualmente el DOM element.textContent = '¡Aplicado!' element.classList.add('is-applied') element.disabled = true }})
Problemas:
- ❌ Manipulación manual del DOM
- ❌ No hay separación entre datos y UI
- ❌ Difícil de testear
- ❌ Propenso a errores
- ❌ Cada botón necesita lógica especial
Ahora (React con useState)
function JobCard({ titulo, empresa, ubicacion, descripcion }) { const [aplicado, setAplicado] = React.useState(false) return ( <button onClick={() => setAplicado(true)} disabled={aplicado}> {aplicado ? '¡Aplicado!' : 'Aplicar'} </button> )}
Ventajas:
- ✅ No manipulas el DOM manualmente
- ✅ El estado y la UI están sincronizados automáticamente
- ✅ Cada componente tiene su propio estado independiente
- ✅ Fácil de testear
- ✅ Declarativo: describes qué quieres, no cómo hacerlo
Cada componente tiene su propio estado
Mira esto: tenemos dos tarjetas de empleo:
<JobCard titulo="Frontend Developer" /><JobCard titulo="Backend Developer" />
Cada una tiene su propio estado independiente:
- Si aplicas al primero, solo el primero cambia
- El segundo mantiene su estado original
- No se interfieren entre sí
¿Por qué?
Porque cada vez que usas <JobCard />, React crea una instancia nueva con su propio estado.
Reglas de useState
1. Solo en el nivel superior
// ✅ Correcto: en el nivel superior del componentefunction JobCard() { const [aplicado, setAplicado] = React.useState(false) return <button>Aplicar</button>}// ❌ Incorrecto: dentro de un iffunction JobCard() { if (algo) { const [aplicado, setAplicado] = React.useState(false) // Error! } return <button>Aplicar</button>}// ❌ Incorrecto: dentro de un loopfunction JobCard() { for (let i = 0; i < 10; i++) { const [aplicado, setAplicado] = React.useState(false) // Error! } return <button>Aplicar</button>}
2. Solo en componentes de React
// ✅ Correcto: en un componentefunction MiComponente() { const [estado, setEstado] = React.useState(0)}// ❌ Incorrecto: en una función normalfunction miFuncion() { const [estado, setEstado] = React.useState(0) // Error!}
3. Nunca modificar el estado directamente
const [aplicado, setAplicado] = React.useState(false)// ❌ Incorrecto: modificar directamenteaplicado = true // No funciona y es un error// ✅ Correcto: usar la función setsetAplicado(true)
Tipos de valores en useState
Puedes usar useState con cualquier tipo de valor:
Booleanos
const [activo, setActivo] = React.useState(false)const [visible, setVisible] = React.useState(true)
Números
const [contador, setContador] = React.useState(0)const [edad, setEdad] = React.useState(25)
Strings
const [nombre, setNombre] = React.useState('')const [mensaje, setMensaje] = React.useState('Hola')
Arrays
const [empleos, setEmpleos] = React.useState([])const [numeros, setNumeros] = React.useState([1, 2, 3])
Objetos
const [usuario, setUsuario] = React.useState({ nombre: 'Miguel', edad: 25 })const [config, setConfig] = React.useState({ tema: 'oscuro', idioma: 'es' })
Ejemplo práctico: Contador
Veamos otro ejemplo simple para entender mejor useState:
function Contador() { const [count, setCount] = React.useState(0) return ( <div> <p>Has hecho clic {count} veces</p> <button onClick={() => setCount(count + 1)}>Incrementar</button> <button onClick={() => setCount(count - 1)}>Decrementar</button> <button onClick={() => setCount(0)}>Resetear</button> </div> )}
¿Qué pasa aquí?
- Estado inicial:
count = 0 - Cada clic actualiza el estado
- React re-renderiza mostrando el nuevo valor
Múltiples estados en un componente
Puedes tener varios estados en el mismo componente:
function JobCard({ titulo, empresa }) { const [aplicado, setAplicado] = React.useState(false) const [guardado, setGuardado] = React.useState(false) const [likes, setLikes] = React.useState(0) return ( <article> <h3>{titulo}</h3> <button onClick={() => setAplicado(true)} disabled={aplicado}> {aplicado ? '¡Aplicado!' : 'Aplicar'} </button> <button onClick={() => setGuardado(!guardado)}> {guardado ? '❤️ Guardado' : '🤍 Guardar'} </button> <button onClick={() => setLikes(likes + 1)}>👍 {likes}</button> </article> )}
Cada estado es independiente y se puede actualizar por separado.
Actualizando estado basado en el valor anterior
A veces necesitas actualizar el estado basándote en su valor anterior:
function Contador() { const [count, setCount] = React.useState(0) // ✅ Forma correcta: usando una función const incrementar = () => { setCount((prevCount) => prevCount + 1) } // ⚠️ Forma simple: funciona pero puede tener problemas const incrementarSimple = () => { setCount(count + 1) } return <button onClick={incrementar}>Incrementar</button>}
¿Cuándo usar la función?
Cuando necesitas hacer múltiples actualizaciones seguidas o cuando el nuevo valor depende del anterior.
Lo que hemos aprendido
En esta clase has aprendido:
- 🧠 Qué es el estado - La memoria del componente
- 🎯 Cómo usar useState - Crear y actualizar estado
- 🔄 Flujo de actualización - Cómo React re-renderiza componentes
- 🎨 Estado en JSX - Usar el estado para cambiar la UI
- ⚡ Comparación con vanilla JS - Mucho más simple y automático
- 🔑 Reglas de useState - Dónde y cómo usarlo
- 📊 Tipos de valores - Booleanos, números, strings, arrays, objetos
- 🔢 Múltiples estados - Varios useState en un componente
En la próxima clase aprenderemos a renderizar listas de elementos dinámicamente usando .map(), para no tener que escribir cada <JobCard /> manualmente.
💡 Recuerda: El estado es la memoria del componente. Cuando el estado cambia, React re-renderiza automáticamente. Nunca modifiques el estado directamente, siempre usa la función
set.
