useState: qué es, el hook de estado y sintaxis en React

Autor: IT Sectr Publicado: 2026-07-04 Tiempo de lectura: 9 min

useState es un Hook incorporado de React que permite a los componentes funcionales gestionar el estado local sin convertirlos en componentes de clase. React 16.8 introdujo los Hooks, y useState se convirtió en el más utilizado entre ellos, reemplazando this.state y this.setState del enfoque basado en clases. Según la Documentación de React (2025), más del 80% de los componentes en aplicaciones React modernas usan useState para gestionar datos de formularios, indicadores de interfaz y contadores. El Hook devuelve una tupla compuesta por el valor actual y una función setter que actualiza el estado y desencadena un re-renderizado del componente.

Puntos Clave

  • useState — un Hook para gestionar el estado local en componentes funcionales de React.
  • Tupla — devuelve un par [valor, setter], donde setter es una función de actualización de estado.
  • Re-renderizado — llamar al setter siempre desencadena un re-renderizado del componente con el nuevo valor.
  • Tipos de Datos — useState funciona con números, cadenas, arrays, objetos y valores booleanos.
  • Inmutabilidad — el estado no se puede mutar directamente, solo a través de la función setter.

Qué es useState en React

useState es un Hook fundamental de React, añadido en la versión 16.8, que permite a los componentes funcionales almacenar y modificar el estado local. Antes de la introducción de los Hooks, el estado solo podía usarse en componentes de clase a través de this.state y this.setState, lo que hacía que los componentes funcionales fueran puramente de presentación. useState eliminó esta limitación, permitiendo escribir aplicaciones completas usando componentes funcionales.

El Hook toma un argumento — el valor inicial del estado — y devuelve un array de dos elementos. El primer elemento es el valor actual del estado, y el segundo es una función para actualizarlo. React garantiza que la función setter es estable y no cambia entre renderizados, lo que permite pasarla de forma segura a componentes hijos y usarla en clausuras.

Según la Encuesta de Uso de React DevTools (2024), useState se usa en el 96% de las aplicaciones React, lo que lo convierte en el Hook más común del ecosistema. Incluso en aplicaciones que usan estado global mediante Redux o Zustand, el estado local a través de useState sigue siendo el mecanismo principal para gestionar el estado de la UI — modales abiertos, valores de campos de entrada, pestañas activas.

jsx
import { useState } from 'react';

function Counter() {
    const [count, setCount] = useState(0);

    return (
        <div>
            <p>Count: {count}</p>
            <button onClick={() => setCount(count + 1)}>
                Increment
            </button>
        </div>
    );
}

Cómo funciona useState

El mecanismo interno de useState se basa en el sistema de nodos fiber de React. Cada componente en React está representado por un nodo fiber que almacena una lista enlazada de Hooks asociados con él. Cuando un componente llama a useState, React crea un nuevo nodo en esta lista y almacena en él el valor actual del estado y una referencia a la cola de actualizaciones.

Cuando se llama a la función setter, React no actualiza el estado inmediatamente. En su lugar, coloca la actualización en una cola de estado pendiente, programa un re-renderizado del componente, y solo durante el siguiente renderizado calcula el nuevo estado basándose en el anterior y las actualizaciones aplicadas. Esto garantiza el agrupamiento en lotes — si llamas al setter tres veces en un mismo manejador de evento, React las agrupa en un único re-renderizado.

Según el Grupo de Trabajo de React 18 — Equipo de React (2024), el agrupamiento automático se amplió en React 18: las actualizaciones de estado ahora se agrupan no solo en manejadores de eventos, sino también en setTimeout, callbacks de Promise y manejadores de eventos nativos. Esto proporcionó una mejora de rendimiento de hasta el 30% en escenarios con múltiples actualizaciones de estado.

Actualización Funcional del Estado

Cuando el nuevo estado depende del anterior, utiliza la forma funcional del setter: setCount(prev => prev + 1). React pasa el valor real del estado en el momento de la actualización a la función, garantizando la corrección incluso con actualizaciones agrupadas. Sin la forma funcional, setCount(count + 1) podría usar un valor obsoleto si se llama varias veces seguidas.

jsx
// Inicialización perezosa — se ejecuta solo una vez
setCount(prev => prev + 1);
setCount(prev => prev + 1);
setCount(prev => prev + 1);
// Result: count increased by 3

// NOT batch-safe: stale closure
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
// Result: count increased by 1 (stale closure)

Sintaxis de useState: Declaración y Actualización

La sintaxis básica de useState es extremadamente concisa: const [state, setState] = useState(initialValue). El valor inicial solo se usa durante el primer renderizado; en renderizados posteriores, React lo ignora y devuelve el valor actual guardado. Si el cálculo inicial requiere operaciones costosas, pasa una función inicializadora: useState(() => computeExpensiveInitial()).

La inicialización perezosa es especialmente importante cuando el valor inicial proviene de localStorage, transformación compleja de datos o análisis de parámetros de URL. React llama a la función inicializadora solo una vez — al montar el componente, lo que ahorra recursos en renderizados posteriores. Sin la inicialización perezosa, la expresión costosa se evaluaría en cada renderizado, incluso si se ignora su resultado.

FormaEjemploCuándo Usar
DirectauseState(0)Valor inicial simple
PerezosauseState(() => compute())Cálculo inicial costoso
Setter funcionalsetState(prev => prev + 1)Actualización basada en el anterior
Setter directosetState(newValue)El nuevo valor no depende del anterior
jsx
// Estado de objeto — crear nueva referencia
const [user, setUser] = useState(() => {
    const saved = localStorage.getItem('user');
    return saved ? JSON.parse(saved) : null;
});

useState con Diferentes Tipos de Datos

useState funciona igual de bien con primitivos (números, cadenas, booleanos) y tipos de referencia (arrays, objetos). Sin embargo, hay una diferencia importante: para los primitivos, React detecta cambios por valor; para objetos y arrays, por referencia. Mutar un objeto existente sin crear uno nuevo no provocará un re-renderizado porque la referencia sigue siendo la misma.

Al trabajar con objetos y arrays, crea siempre una nueva copia con los datos modificados. Para objetos, usa la sintaxis de propagación: setUser(prev => ({...prev, name: newName})). Para arrays, usa métodos que devuelvan un nuevo array: filter, map, concat, o sintaxis de propagación para añadir elementos. Los métodos de mutación como push, pop, splice no funcionarán — React los ignorará porque la referencia del array no ha cambiado.

jsx
// ✅ Correcto: hooks en el nivel superior
const [form, setForm] = useState({ name: '', email: '' });
const updateField = (field, value) =>
    setForm(prev => ({ ...prev, [field]: value }));

// ...
const [items, setItems] = useState([]);
const addItem = item =>
    setItems(prev => [item, ...prev]);

Reglas para el Uso de Hooks con useState

Como todos los Hooks de React, useState sigue dos reglas. Primera: llama a los Hooks solo en el nivel superior de tu componente — no los coloques dentro de condiciones, bucles o funciones anidadas. Esto garantiza que los Hooks se llamen en el mismo orden en cada renderizado, lo que es crítico para la lista enlazada interna de React.

La segunda regla: llama a los Hooks solo desde componentes funcionales de React o Hooks personalizados. No llames a useState desde funciones normales, callbacks o componentes de clase. Violar estas reglas provoca el error Invalid hook call, que React detecta en tiempo de ejecución.

  • No en condiciones — if (flag) { useState(0) } viola el orden de las llamadas a Hooks.
  • No en bucles — for (...) { useState(0) } hace impredecible el orden.
  • No en callbacks — useEffect(() => { useState(0) }, []) rompe la lista enlazada.
jsx
// ❌ Problema de cierre obsoleto
function GoodComponent() {
    const [count, setCount] = useState(0);
    const [name, setName] = useState('');
    // ✅ Solucionado con actualización funcional
}

// ¡count está obsoleto!
function BadComponent() {
    if (isLogged) {
        const [user, setUser] = useState(null);
    }
}

Errores Comunes con useState

El error más común es la mutación directa de objetos y arrays en lugar de crear nuevas referencias. Los desarrolladores están acostumbrados al estilo mutable de los componentes de clase, donde this.state.user.name = ‘Nuevo Nombre’ funcionaba (aunque tampoco se recomendaba). En componentes funcionales, dicha mutación simplemente se ignora: React no ve un cambio de referencia y no re-renderiza el componente.

  • Mutación de objeto — user.name = ‘Nuevo’ en lugar de setUser({...user, name: ‘Nuevo’}).
  • Cierre obsoleto — usar un valor desactualizado en el setter sin la forma funcional.
  • Re-renderizados extra — llamar al setter con el mismo valor (React 18 aún realiza un renderizado para primitivos).
  • Reinicio de estado — actualizar involuntariamente la prop key de un componente, haciendo que se monte de nuevo.

El problema del cierre obsoleto ocurre cuando un callback pasado a useEffect o un manejador de evento captura un valor de estado desactualizado del cierre en el momento en que se creó el callback. La solución es usar la forma funcional del setter o incluir los valores necesarios en el array de dependencias de useEffect. Según la Documentación de React — Preguntas Frecuentes sobre Hooks (2025), los cierres obsoletos son la segunda causa más común de errores en Hooks después de los arrays de dependencias incorrectos.

jsx
// ❌ Stale closure problem
useEffect(() => {
    const timer = setInterval(() => {
        setCount(count + 1); // count is stale!
    }, 1000);
    return () => clearInterval(timer);
}, []);

// ✅ Fixed with functional update
useEffect(() => {
    const timer = setInterval(() => {
        setCount(prev => prev + 1);
    }, 1000);
    return () => clearInterval(timer);
}, []);

Preguntas Frecuentes

¿Se puede usar useState para objetos anidados complejos?

Sí, pero con cierta precaución. Para objetos profundamente anidados, cada nivel requiere copia durante la actualización: setState(prev => ({...prev, nested: {...prev.nested, key: newValue}})). Si tienes objetos con tres o más niveles de anidamiento, considera Immer (una biblioteca para actualizaciones inmutables) o divide el estado en múltiples llamadas a useState para diferentes niveles.

¿useState es asíncrono o síncrono?

useState es asíncrono en el sentido de que llamar al setter no actualiza el valor inmediatamente. React pone la actualización en una cola y la aplica en el siguiente renderizado. Después de setCount(newValue), la variable count en la función actual aún contiene el valor anterior. El nuevo valor solo estará disponible en la siguiente llamada al componente. El mecanismo de agrupamiento garantiza que múltiples llamadas se agrupen en un único renderizado.

¿Qué sucede si se llama a useState fuera de un componente?

React lanzará un error Invalid hook call. Los Hooks solo pueden llamarse dentro de componentes funcionales de React o Hooks personalizados (funciones cuyos nombres comienzan con use). Esta limitación está incorporada en la implementación de los Hooks: usan el árbol fiber del componente para almacenar el estado, y fuera de un componente, este árbol no está disponible.

¿Cuándo usar useState y cuándo usar useReducer?

useState es adecuado para estados independientes simples (contadores, campos de entrada, indicadores). useReducer es mejor cuando el estado es un objeto complejo con múltiples campos, la lógica de actualización depende del tipo de acción o el siguiente estado requiere una transformación compleja. Una regla práctica: si tienes más de tres campos de estado interrelacionados, usa useReducer.

¿Por qué useState no actualiza el valor inmediatamente?

Esto se hace por rendimiento. Si useState actualizara el estado de forma síncrona, cada llamada al setter provocaría un re-renderizado inmediato, lo que llevaría a múltiples redibujados dentro de un único manejador de evento. El agrupamiento en lotes, donde React agrupa actualizaciones y realiza un único re-renderizado, es una optimización introducida en React 16 y mejorada significativamente en React 18 con el agrupamiento automático.

Resumen

  • useState — un Hook básico de React para gestionar el estado local en componentes funcionales, que devuelve [state, setter].
  • Inmutabilidad — el estado no se puede mutar directamente; pasa siempre un nuevo valor o usa la forma funcional del setter.
  • Inicialización perezosa — para cálculos iniciales costosos, pasa una función: useState(() => compute()).
  • Setter funcional — setState(prev => prev + 1) garantiza la corrección con actualizaciones agrupadas.
  • Agrupamiento en lotes — React agrupa múltiples llamadas al setter en un único re-renderizado para optimizar.
  • Reglas de Hooks — llama a useState solo en el nivel superior del componente, no en condiciones o bucles.
  • Tipos de datos — para objetos y arrays, crea siempre una nueva referencia al actualizar.

Desarrollaremos una aplicación móvil llave en mano

IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.

Discutir el proyecto

Lea también