useState Hook
Volver a clases
Fundamentos de ProgramaciónFrontend DevelopmentPrincipiante

useState Hook

120 min

Manejo de estado local con `useState` y actualización correcta.

Qué es el estado

Estado es todo dato que, al cambiar, debe repintar la pantalla. Esa es la definición operativa y el criterio para decidir qué guardar en useState y qué no.

jsx
1const [count, setCount] = useState(0);
2//     ↑        ↑                   ↑
3//   valor    setter        valor inicial (solo se usa en el primer render)

Lo que hace especial al setter no es que cambie una variable: es que le avisa a React. Una variable normal cambiaría su valor y la pantalla seguiría igual, porque nadie le dijo a React que volviera a dibujar.

La regla que gobierna todo: inmutabilidad

React decide si repintar comparando referencias, no contenido. Si le pasas el mismo objeto o array que ya tenía, concluye "no cambió nada" y no repinta — aunque tú lo hayas modificado por dentro.

jsx
1// ❌ MUTACIÓN: mismo array, misma referencia → React no repinta
2tareas.push(nueva);
3setTareas(tareas);
4
5// ✅ array NUEVO → React detecta el cambio
6setTareas([...tareas, nueva]);

De aquí sale el patrón de todas las operaciones sobre listas:

jsx
1// Agregar
2setTareas([...tareas, nueva]);
3
4// Eliminar
5setTareas(tareas.filter((t) => t.id !== id));
6
7// Modificar uno (¡ojo: también se copia el objeto interno!)
8setTareas(tareas.map((t) => (t.id === id ? { ...t, hecha: !t.hecha } : t)));

Ese último es el que más se falla: hacer spread del array pero mutar el objeto de adentro (t.hecha = !t.hecha) deja el mismo objeto interno, y con componentes memoizados o en modo estricto el bug reaparece.

Regla mental: en React nunca modificas lo que existe, construyes la versión nueva.

Actualizaciones por lote y el updater

setState no es instantáneo: React agrupa las actualizaciones y las aplica antes del siguiente render. Por eso esto no hace lo que parece:

jsx
1setCount(count + 1);
2setCount(count + 1);   // 🚨 ambas leyeron el mismo count → avanza 1, no 2

Cuando el nuevo valor depende del anterior, usa la forma de función (updater), que recibe el valor más reciente:

jsx
1setCount((prev) => prev + 1);
2setCount((prev) => prev + 1);   // ✅ avanza 2

La misma trampa aparece al leer el estado justo después de actualizarlo: setCount(5); console.log(count) imprime el valor viejo, porque count es una constante de este render. Si necesitas el valor nuevo, ya lo tienes: es lo que le pasaste al setter.

Reglas básicas de uso del hook

  • Solo se llama en el nivel superior del componente (no dentro de loops, condicionales o funciones anidadas).
  • Solo se usa dentro de componentes de React o custom hooks.
  • Mantener el orden de los hooks estable evita bugs con el estado.

Inicialización perezosa

  • Cuando el estado inicial es costoso, pasa una función a useState.
  • React la ejecuta una sola vez en el primer render.

Estado derivado y fuente de verdad

  • Evita duplicar datos que se pueden calcular desde otros estados/props.
  • Prefiere un único estado "fuente de verdad" y deriva el resto en render.

Actualizaciones en lote (batching)

  • setState es asíncrono; no confíes en el valor inmediato después de actualizar.
  • React puede agrupar múltiples actualizaciones para mejorar rendimiento.

Errores comunes a evitar

  • Mutar el estado directamente (obj.prop = ...).
  • Reemplazar un objeto sin conservar propiedades que necesitas.
  • Usar índices como key cuando la lista cambia de orden o se edita.

Caso práctico: Todo App

  • CRUD de tareas con filtros y contador.

Ejemplos de Código

5 ejemplos

Estado básico

javascript
1const [count, setCount] = useState(0);

Estado con objeto

javascript
1const [user, setUser] = useState({ nombre: "", email: "" });
2setUser((prev) => ({ ...prev, email: "test@mail.com" }));

Estado con arrays

javascript
1const [items, setItems] = useState([]);
2setItems([...items, { id: Date.now(), texto: "Nueva" }]);

Lazy init

javascript
1const [count, setCount] = useState(() => {
2  const saved = localStorage.getItem("count");
3  return saved ? Number(saved) : 0;
4});

Toggle state

javascript
1const [isOpen, setIsOpen] = useState(false);
2setIsOpen((prev) => !prev);

Recursos

3 recursos disponibles

¡Hora de Practicar!

PrácticaPrincipiante15 min🟡 IA con bitácora

Práctica guiada - Todo App

Práctica

Crear una Todo App con agregar, completar y eliminar tareas.

Desafío de Código

EjercicioPrincipiante15 min🔴 Sin IA

Ejercicios - useState

Ejercicios

(1) Contador con +3, (2) formulario controlado, (3) lista editable.

Documentación Oficial

DocumentaciónPrincipiante15 min

useState Hook

Explicación oficial del hook useState.