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 valor
const 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!”
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 elemento
const jobsListingSection = document.querySelector('.jobs-listings')
// Añadir evento
jobsListingSection.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 componente
function JobCard() {
const [aplicado, setAplicado] = React.useState(false)
return <button>Aplicar</button>
}
// ❌ Incorrecto: dentro de un if
function JobCard() {
if (algo) {
const [aplicado, setAplicado] = React.useState(false) // Error!
}
return <button>Aplicar</button>
}
// ❌ Incorrecto: dentro de un loop
function 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 componente
function MiComponente() {
const [estado, setEstado] = React.useState(0)
}
// ❌ Incorrecto: en una función normal
function miFuncion() {
const [estado, setEstado] = React.useState(0) // Error!
}
3. Nunca modificar el estado directamente
const [aplicado, setAplicado] = React.useState(false)
// ❌ Incorrecto: modificar directamente
aplicado = true // No funciona y es un error
// ✅ Correcto: usar la función set
setAplicado(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.