Menu

Hook useState en React: ejemplos y errores comunes

useState le da a un componente un valor que sobrevive a los renderizados y una función que vuelve a dibujar la pantalla cuando el valor cambia. Aprende la sintaxis, las funciones de actualización, objetos y arrays en el estado, y por qué un valor parece desactualizado justo después de cambiarlo.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

useState es el hook que permite a un componente recordar algo entre renderizados: un contador, el texto de un input, si un menú está abierto. Cada vez que llamas a su setter, React vuelve a renderizar el componente con el nuevo valor.

Haz clic en el botón y luego cambia useState(0) por useState(10) en el editor. La vista previa se reconstruye y el contador empieza en 10.

La sintaxis

const [value, setValue] = useState(initialValue);
  • initialValue solo se usa en el primer renderizado. En cada renderizado posterior React lo ignora y te devuelve el valor que guardó.
  • value es el valor actual para este renderizado.
  • setValue reemplaza el valor y programa un nuevo renderizado.

Los nombres los eliges tú. useState devuelve un array de dos elementos y los corchetes lo desempaquetan, así que [isOpen, setIsOpen] y [email, setEmail] son el mismo patrón.

Llama a useState en el nivel superior de tu componente, nunca dentro de un bucle, una condición o una función anidada. React asocia cada llamada con su valor guardado por el orden de las llamadas, así que ese orden debe ser el mismo en cada renderizado (consulta las reglas de los hooks).

Varias piezas de estado

Un componente puede llamar a useState tantas veces como necesite. Cada llamada es un valor propio e independiente.

El estado es una instantánea

El setter no cambia la variable que tienes en las manos. Le dice a React cuál debe ser el valor en el siguiente renderizado. Hasta que ocurra ese renderizado, el actual sigue viendo el valor viejo.

Abre la consola bajo la vista previa y haz clic una vez. El manejador registra 0, el valor viejo, y después el componente registra un nuevo renderizado con 1. No hay ningún error: count es una constante durante todo el renderizado, y el siguiente renderizado recibe una nueva.

Funciones de actualización

Por la instantánea, llamar al setter tres veces con count + 1 suma uno, no tres: cada llamada calcula 0 + 1. Si pasas una función, React le da a cada llamada el resultado de la anterior.

Usa la forma de actualización siempre que el siguiente valor dependa del anterior y la actualización pueda no ejecutarse directamente desde el último renderizado: varios sets en un manejador, un callback de setInterval o un efecto.

Objetos y arrays en el estado

React decide si volver a renderizar comparando el valor viejo y el nuevo con Object.is. Si modificas un objeto en su lugar y devuelves el mismo objeto, la comparación dice que nada cambió y la pantalla se queda como estaba. Pasa siempre un objeto o array nuevo.

{ ...user, age: user.age + 1 } copia todos los campos y sobrescribe uno. [...tags, 'code'] agrega un elemento y filter quita uno, y ambos devuelven un array nuevo. La página sobre actualizar arrays y objetos cubre objetos anidados, editar un elemento de una lista e insertar en medio.

Estado inicial diferido

El argumento de useState se evalúa en cada renderizado, aunque React solo lo usa la primera vez. Cuando calcularlo es costoso (leer localStorage, analizar una cadena grande), pasa una función. React la llama una sola vez, en el primer renderizado.

// Runs readSavedNotes() on every render, then throws the result away
const [notes, setNotes] = useState(readSavedNotes());

// Runs it once
const [notes, setNotes] = useState(readSavedNotes);
const [notes, setNotes] = useState(() => readSavedNotes());

Reiniciar el estado con una key

El estado pertenece a la posición de un componente en el árbol. Si renderizas una key distinta en el mismo lugar, React lo trata como un componente nuevo y su estado empieza de cero. Es la forma más limpia de reiniciar un formulario cuando cambia lo que edita.

Escribe un mensaje y luego cambia el destinatario: el borrador se vacía. Borra key={to} y vuelve a intentarlo, y el texto se queda, porque ahora React ve el mismo Draft en el mismo lugar.

Errores comunes

Registrar el estado justo después de cambiarlo. Ves el valor viejo, como se mostró arriba. Registra en el cuerpo del componente, o calcula primero el siguiente valor en una variable y usa esa.

Mutar el estado. user.age++; setUser(user) pasa el mismo objeto, así que no se renderiza nada. Copia primero.

Llamar al setter durante el renderizado. <button onClick={setCount(count + 1)}> llama a setCount mientras renderiza, lo que vuelve a renderizar, lo que lo vuelve a llamar. React detiene este bucle con "Too many re-renders". Pasa una función: onClick={() => setCount(count + 1)}.

Guardar valores que puedes calcular. Si fullName siempre es first + ' ' + last, calcúlalo durante el renderizado en lugar de mantener una tercera pieza de estado que se puede desincronizar.

Preguntas frecuentes

¿Qué devuelve useState?

Un array con exactamente dos elementos: el valor actual y una función que lo reemplaza. Tú les pones nombre con desestructuración de arrays, por convención [thing, setThing].

¿Por qué mi estado no se actualiza de inmediato?

Llamar al setter no cambia la variable que ya tienes. Le pide a React que vuelva a renderizar el componente, y el nuevo valor aparece en ese siguiente renderizado. Dentro del manejador de eventos actual el valor viejo sigue en el ámbito, así que console.log(count) justo después de setCount(count + 1) imprime el número anterior.

¿Cuándo debo pasarle una función al setter?

Cuando el nuevo valor depende del anterior y lo cambias más de una vez, o desde un temporizador o un efecto. setCount(c => c + 1) siempre parte del valor más reciente, mientras que setCount(count + 1) parte del valor que vio este renderizado.

¿useState puede guardar un objeto o un array?

Sí, pero trátalo como de solo lectura. Crea un objeto o array nuevo y pásalo al setter (setUser({ ...user, name }), setItems([...items, item])). Modificar el existente en su lugar no provoca un renderizado.

¿Cuál es la diferencia entre useState y useReducer?

Ambos guardan estado. useState sirve para unos pocos valores independientes. useReducer lleva la lógica de actualización a una sola función que recibe una acción, lo que se lee mejor cuando muchos eventos cambian el mismo estado de formas relacionadas.

Ilustración de los lenguajes de programación de Coddy

Aprende a programar con Coddy

COMENZAR