Ir al contenido
angel.library

useState

Cada vez que un componente necesita “recordar” algo entre renders y que la UI reaccione cuando cambia, es useState. Es la base sobre la que se explican todos los demás hooks de estado (useReducer, useContext para compartirlo, useRef para lo que no necesita re-renderizar).

useState devuelve un par: el valor actual y una función para actualizarlo. Llamar a esa función programa un re-render con el nuevo valor — nunca muta la variable directamente.

import { useState } from "react"
function Contador() {
const [cuenta, setCuenta] = useState(0)
return <button onClick={() => setCuenta(cuenta + 1)}>{cuenta}</button>
}

setCuenta(cuenta + 1) usa el valor de cuenta capturado en ese render. Si el nuevo valor depende del anterior y puedes llamar al setter varias veces seguidas (o desde un callback async), la forma funcional evita leer un valor desactualizado.

setCuenta(cuenta + 1) // usa el "cuenta" de este render
setCuenta((prev) => prev + 1) // usa siempre el valor más reciente, aunque se llame varias veces seguidas
function incrementarDosVeces() {
setCuenta((prev) => prev + 1)
setCuenta((prev) => prev + 1) // con la forma directa, esto NO sumaría 2
}

El error más común: mutar objetos o arrays

Sección titulada «El error más común: mutar objetos o arrays»

React compara el estado anterior con el nuevo por referencia, no por contenido profundo. Mutar el objeto/array existente y volver a pasarlo no dispara re-render — la referencia es la misma, React asume que no cambió nada.

const [usuario, setUsuario] = useState({ nombre: "Ana", edad: 30 })
// 🔴 Muta el objeto existente — React no detecta el cambio
usuario.edad = 31
setUsuario(usuario)
// ✅ Objeto nuevo, con el resto de propiedades copiadas
setUsuario({ ...usuario, edad: 31 })

Mismo caso con arrays: push/splice/sort mutan en el lugar — [...array, nuevoItem], array.filter(...), array.map(...) devuelven uno nuevo.

Si el valor inicial requiere un cálculo costoso, pasarlo como función evita que se ejecute en cada render — useState solo la llama una vez, en el primer render.

const [datos, setDatos] = useState(() => calcularAlgoCostoso())
UsoEjemplo
Declarar estadoconst [valor, setValor] = useState(inicial)
Actualizar con el valor de este rendersetValor(nuevoValor)
Actualizar basado en el valor más recientesetValor(prev => prev + 1)
Inicialización perezosauseState(() => calculoInicialCostoso())
Objeto/array nuevo, no mutado{ ...obj, campo: nuevo }, [...arr, item]
  • setValor(mismoValor) con el mismo valor primitivo no dispara re-render — React lo compara con Object.is antes de programar la actualización.
  • Varias llamadas a setValor en el mismo handler se agrupan (batching): React re-renderiza una sola vez al final, no una vez por cada setValor.
  • Cuando la lógica de “cómo cambia” el estado se vuelve compleja (varios campos relacionados, varias formas de actualizar), es momento de mirar useReducer en vez de seguir sumando useState sueltos.