Saltar al contenido principal
Logo de JSCamp

Estado con useState

Aprende a gestionar el estado de tus componentes con useState y crear interfaces interactivas

midudev

Más clases y directos en YouTube

Suscribirse

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?

  1. React.useState(false) - Crea una variable de estado con valor inicial false
  2. aplicado - Variable que contiene el valor actual del estado
  3. setAplicado - 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

  1. Estado inicial: aplicado = false
  2. Usuario hace clic en el botón
  3. Se ejecuta handleAplicar
  4. Se llama setAplicado(true)
  5. React actualiza aplicado a true
  6. React re-renderiza el componente
  7. 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:

  1. className - Si aplicado es true, añade la clase is-applied
  2. disabled - Deshabilita el botón cuando aplicado es true
  3. 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 disabled también se anuncia correctamente. Si necesitas anunciar cambios más complejos o importantes, considera usar aria-live regions o aria-label para 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í?

  1. Estado inicial: count = 0
  2. Cada clic actualiza el estado
  3. 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.