React
useState
Explica cómo useState conserva estado local, programa actualizaciones, acepta inicialización diferida y permite calcular el siguiente valor con funciones updater.
- Última actualización
- Actualizada
- Nivel
- Fundamentos
React
Explica cómo useState conserva estado local, programa actualizaciones, acepta inicialización diferida y permite calcular el siguiente valor con funciones updater.
useState permite que una instancia de componente recuerde información entre renders. El setter solicita un nuevo render; no modifica la variable del render actual.
const [count, setCount] = useState(0);El estado inicial se usa al montar la instancia.
Si calcular el valor inicial es costoso, pasa una función:
const [draft, setDraft] = useState(() => loadDraft());React llama al inicializador durante la creación de la instancia, no en cada render.
setCount((current) => current + 1);Usa esta forma cuando el siguiente valor depende del anterior. Los actualizadores se procesan en orden dentro de la cola de actualizaciones.
const [user, setUser] = useState({ name: "Ana", age: 25 });
setUser((current) => ({
...current,
age: 26,
}));A diferencia del antiguo setState de clases, el setter reemplaza el valor completo.
Prefiere estados que representen el mínimo necesario:
const [query, setQuery] = useState("");
const visibleProducts = products.filter((product) =>
product.name.includes(query),
);No guardes visibleProducts si puede calcularse.
Agrupa valores que siempre cambian juntos. Separa valores independientes. Si existen muchas transiciones relacionadas, considera useReducer.
El estado no se reinicia porque una prop cambie. Para representar una identidad nueva, cambia la key o diseña un componente controlado.
¿Por qué tres llamadas a setCount(count + 1) no garantizan sumar tres?
Porque las tres leen el mismo count del render actual. Para acumular, usa tres actualizadores funcionales.
useState registra una celda de memoria asociada con la instancia actual. En cada render React devuelve el snapshot correspondiente y un setter estable para solicitar otro valor.
render actual
↓ lee snapshot
[count, setCount]
↓ evento llama setter
cola de actualizaciones
↓
nuevo render con otro snapshotEl setter no modifica la variable que ya fue leída:
function Counter() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
console.log(count); // snapshot anterior
}
return <button onClick={handleClick}>{count}</button>;
}Un valor debería vivir en state cuando:
No merece state un timer ID, un nodo DOM o un valor que solo usa un handler; normalmente pertenecen a refs o variables locales.
const [products, setProducts] = useState<Product[]>([]);
const [query, setQuery] = useState("");
const visibleProducts = products.filter((product) =>
product.name.toLowerCase().includes(query.toLowerCase()),
);Guardar también visibleProducts crea otra fuente de verdad. Cada estado adicional aumenta combinaciones posibles y sincronizaciones necesarias.
const [settings] = useState(createDefaultSettings());Aquí createDefaultSettings() se ejecuta en cada render, aunque React solo utilice el resultado inicial al montar.
const [settings] = useState(createDefaultSettings);La función se usa como inicializador perezoso. Debe ser pura. Strict Mode puede llamarla adicionalmente en desarrollo para detectar impurezas.
Cuando quieres almacenar una función como valor, envuélvela:
const [formatter] = useState(() => formatCurrency);El setter reemplaza el valor completo:
setUser({ name: "Laura" });No conserva automáticamente otras propiedades. Para actualizar un objeto:
setUser((current) => ({
...current,
name: "Laura",
}));La copia debe alcanzar cada nivel modificado. No mutar mantiene snapshots anteriores coherentes y permite comparar referencias.
Usa valor directo cuando no depende del anterior:
setStatus("success");Usa updater cuando sí depende:
setQuantity((current) => current + 1);Esto es especialmente importante con varias actualizaciones agrupadas o callbacks que se ejecutan después.
setCount((current) => current + 1);
setCount((current) => current + 1);
setCount((current) => current + 1);React procesa cada updater usando el resultado anterior.
Si el siguiente valor es igual al actual según Object.is, React puede omitir trabajo:
setCount(3); // si ya era 3Mutar un objeto y reenviar la misma referencia puede ocultar el cambio:
user.name = "Laura";
setUser(user); // referencia igualProduce un objeto nuevo.
Separa valores que cambian independientemente:
const [query, setQuery] = useState("");
const [isOpen, setIsOpen] = useState(false);Agrupa cuando representan una transición indivisible:
type RequestState =
| { status: "idle" }
| { status: "loading" }
| { status: "success"; data: Product[] }
| { status: "error"; message: string };Esta unión evita combinaciones como isLoading=true, error presente y datos “exitosos” al mismo tiempo.
function Editor({ initialText }: { initialText: string }) {
const [text, setText] = useState(initialText);
}initialText solo se lee al crear la instancia. El nombre initial debe comunicar esa semántica. Si el valor debe seguir al padre, usa un componente controlado. Si cambia la entidad, una key puede reiniciar la instancia.
Cada handler ve el snapshot del render que lo creó:
function handleDelayedAlert() {
setTimeout(() => alert(count), 3000);
}Aunque count cambie después, el callback conserva el valor anterior. Esto es una closure normal de JavaScript. Si necesitas leer el valor más reciente sin renderizar, evalúa una ref; si el siguiente estado depende del anterior, usa updater.
Llamar setters incondicionalmente durante render produce un ciclo:
function Example() {
const [count, setCount] = useState(0);
setCount(count + 1); // incorrecto
return count;
}Las actualizaciones pertenecen a eventos, Actions, effects de sincronización o lógica controlada por React. No uses state para corregir el resultado que ya puedes calcular.
useState sigue siendo válido para objetos complejos. Considera reducer cuando:
No cambies solo porque “hay más de tres estados”.
Cuando un estado no cambia:
Cuando cambia demasiado:
useState recuerda información por instancia.useState(loadData()) ejecuta loadData más veces de las necesarias?State snapshots y batching profundiza en las colas de actualización, closures y agrupación de trabajo.