useState
useState
es un React Hook que le permite agregar una variable de estado a su componente.
const [estado, setEstado] = useState(estadoInicial)
- Uso
- Reference
- Solución de problemas
- He actualizado el estado, pero el registro me da el valor anterior
- He actualizado el estado, pero la pantalla no se actualiza
- Recibo un error: “Demasiados renderizados”
- Mi función de inicializador o actualizador se ejecuta dos veces
- Estoy tratando de establecer el estado de una función, pero es llamado en su lugar
Uso
Agregar estado a un componente
Llamar useState
en el nivel superior de su componente para declarar una o más variables de estado.
import { useState } from 'react';
function MyComponent() {
const [edad, setEdad] = useState(42);
const [nombre, setNombre] = useState('Taylor');
// ...
La convención es nombrar variables de estado como [algo, setAlgo]
utilizando la desestructuración de matrices.
useState
devuelve un array con exactamente dos elementos:
- El estado actual de esta variable de estado, establecida inicialmente en el estado inicial que proporcionó.
- La función
set
que le permite cambiarlo a cualquier otro valor en respuesta a la interacción.
Para actualizar lo que está en la pantalla, llame a la función set con algún estado:
function handleClick() {
setNombre('Robin');
}
React almacenará el siguiente estado, renderizará su componente nuevamente con los nuevos valores y actualizará la interfaz de usuario.
Ejemplo 1 de 4: Contador (número)
En este ejemplo, la variable contador
contiene un número. Al hacer click en el botón lo incrementa
import { useState } from 'react'; export default function Contador() { const [contador, setContador] = useState(0); function handleClick() { setContador(contador + 1); } return ( <button onClick={handleClick}> Me presionaste {contador} veces </button> ); }
Estado de actualización basado en el estado anterior
Supongamos que edad
es 42
. La función handler
llama setEdad(edad + 1)
tres veces:
function handleClick() {
setEdad(edad + 1); // setEdad(42 + 1)
setEdad(edad + 1); // setEdad(42 + 1)
setEdad(edad + 1); // setEdad(42 + 1)
}
Sin embargo, después de un click, edad
solo será 43
en lugar de 45! Esto se debe a que llamar a la función set
no actualizará la variable de estado edad
en el código que ya se está ejecutando. Así que cada llamada setAge(age + 1)
se convierte en setEdad(43)
.
Para resolver este problema, puede pasar una función de actualización a setEdad
en lugar del siguiente estado:
function handleClick() {
setEdad(e => e + 1); // setEdad(42 => 43)
setEdad(e => e + 1); // setEdad(43 => 44)
setEdad(e => e + 1); // setEdad(44 => 45)
}
Aqui, e => e + 1
es la función de actualización. Toma el estado pendiente y calcula el siguiente estado a partir de él.
React pone sus funciones de actualización en una cola. Entonces, durante el siguiente renderizado, las llamara en el mismo orden:
e => e + 1
recibirá42
como estado pendiente y devolverá43
como el siguiente estado.e => e + 1
recibirá43
como estado pendiente y devolverá44
como el siguiente estado.e => e + 1
recibirá44
como estado pendiente y devolverá45
como el siguiente estado.
No hay otras actualizaciones en cola, por lo que React almacenará 45
como el estado actual al final.
Por convención, es común nombrar el argumento de estado pendiente como la primera letra del nombre de la variable de estado, como e
para edad
. No obstante, también puedes llamarlo como prevEdad
o cualquier otra cosa que te resulte más clara.
React puede llamar a sus actualizadores dos veces en desarrollo para verificar que sean puros.
Deep Dive
Es posible que escuches una recomendación para escribir siempre código como setEdad(e => e + 1)
si el estado que está configurando se calcula a partir del estado anterior. No hay daño en ello, pero tampoco siempre es necesario.
En la mayoría de los casos, no hay diferencia entre estos dos enfoques. React siempre se asegura de que para las acciones intencionales del usuario, como los click, la variable de estado edad
se actualizará antes del siguiente click. Esto significa que no hay riesgo de que un controlador de clicks vea un mensaje “obsoleto” de edad
al comienzo del controlador de eventos.
Sin embargo, si realiza varias actualizaciones dentro del mismo evento, los actualizadores pueden ser útiles. También son útiles si acceder a la variable de estado en sí es un inconveniente (es posible que te encuentres con esto al optimizar los renderizados).
Si prefiere la coherencia a una sintaxis un poco más detallada, es razonable escribir siempre un actualizador si el estado que está configurando se calcula a partir del estado anterior. Si se calcula a partir del estado anterior de alguna otra variable de estado, es posible que desee combinarlos en un solo objeto y uses un reducer.
Ejemplo 1 de 2: Pasar la funcion de actualización
Este ejemplo pasa la función de actualización, por lo que funciona el botón “+3”.
import { useState } from 'react'; export default function Contador() { const [edad, setEdad] = useState(42); function incremento() { setEdad(a => a + 1); } return ( <> <h1>Tu edad: {edad}</h1> <button onClick={() => { incremento(); incremento(); incremento(); }}>+3</button> <button onClick={() => { incremento(); }}>+1</button> </> ); }
Actualización de objetos y matrices en el estado
Se pueden poner objetos y matrices en el estado. En React, el estado se considera de solo lectura, por lo que debe reemplazarse en lugar de mutar sus objetos existentes . Por ejemplo, si tienes un objeto formulario
en el estado, no lo actualices así:
// 🚩 No cambies un objeto en un estado como este:
formulario.primernombre = 'Taylor';
En su lugar, reemplace todo el objeto creando uno nuevo:
// ✅ Reemplaza el estado con un nuevo objeto
setFormulario({
...formulario,
primernombre: 'Taylor'
});
Lea updating objects in state and updating arrays in state to learn more.
Ejemplo 1 de 4: Formulario (object)
En este ejemplo, la variable de estado formulario
contiene un objeto. Cada entrada tiene un controlador de cambios que llama setFormulario
con el siguiente estado de todo el formulario. La sintaxis extendida { ...formulario }
garantiza que el objeto de estado se reemplace en lugar de mutar.
import { useState } from 'react'; export default function Formulario() { const [formulario, setFormulario] = useState({ nombre: 'Barbara', apellido: 'Hepworth', email: 'bhepworth@sculpture.com', }); return ( <> <label> Nombre: <input value={formulario.nombre} onChange={e => { setFormulario({ ...formulario, nombre: e.target.value }); }} /> </label> <label> Apellido: <input value={formulario.apellido} onChange={e => { setForm({ ...formulario, apellido: e.target.value }); }} /> </label> <label> Email: <input value={formulario.email} onChange={e => { setForm({ ...formulario, email: e.target.value }); }} /> </label> <p> {formulario.nombre}{' '} {formulario.apellido}{' '} ({formulario.email}) </p> </> ); }
Evitar recrear el estado inicial
React guarda el estado inicial una vez y lo ignora en los próximos renderizados.
function TodoList() {
const [todos, setTodos] = useState(crearIniciarTodos());
// ...
Aunque el resultado de crearIniciarTodos()
solo se usa para el renderizado inicial, todavía está llamando a esta función en cada renderizado. Esto puede ser un desperdicio si se trata de crear matrices grandes o realizar cálculos costosos.
Para resolver esto, puede pasarlo como una función initializer a useState
en su lugar:
function TodoList() {
const [todos, setTodos] = useState(crearInicialTodos);
// ...
Observa que está pasando crearIniciarTodos
, que es la función misma, y no crearIniciarTodos()
, que es el resultado de llamarla. Si pasa una función a useState
, React solo la llamará durante la inicialización.
React puede llamar a sus inicializadores dos veces en desarrollo para verificar que sean puros.
Ejemplo 1 de 2: Pasando la función de inicializador
Este ejemplo pasa la función de inicialización, por lo que la función crearIniciarTodos
solo se ejecuta durante la inicialización. No se ejecuta cuando el componente se vuelve a renderizar, como cuando escribe en la entrada.
import { useState } from 'react'; function crearIniciarTodos() { const initialTodos = []; for (let i = 0; i < 50; i++) { initialTodos.push({ id: i, texto: 'Item ' + (i + 1) }); } return initialTodos; } export default function TodoList() { const [todos, setTodos] = useState(crearIniciarTodos); const [texto, setTexto] = useState(''); return ( <> <input value={texto} onChange={e => setText(e.target.value)} /> <button onClick={() => { setTexto(''); setTodos([{ id: todos.length, texto: texto }, ...todos]); }}>Agregar</button> <ul> {todos.map(item => ( <li key={item.id}> {item.texto} </li> ))} </ul> </> ); }
Restablecimiento de estado con una key
Por lo general, es posible que encuentre el atributo key
al representar listas. Sin embargo, también tiene otro propósito.
Puede restablecer el estado de un componente pasando una key
diferente a un componente. En este ejemplo, el botón Restablecer cambia la variable de estado versión
, que pasamos como una key
al Formulario
. Cuando la key
cambia, React vuelve a crear el componente Formulario
(y todos sus elementos secundarios) desde cero, por lo que su estado se restablece.
Lea preservar y restablecer el estado para obtener más información.
import { useState } from 'react'; export default function App() { const [version, setVersion] = useState(0); function handleReset() { setVersion(version + 1); } return ( <> <button onClick={handleReset}>Reset</button> <Formulario key={version} /> </> ); } function Formulario() { const [nombre, setNombre] = useState('Taylor'); return ( <> <input value={nombre} onChange={e => setNombre(e.target.value)} /> <p>Hola, {nombre}.</p> </> ); }
Almacenamiento de información de renders anteriores
Por lo general, actualizará el estado en los controladores de eventos. Sin embargo, en casos excepcionales, es posible que desee ajustar el estado en respuesta a la representación; por ejemplo, es posible que desee cambiar una variable de estado cuando cambia una propiedad.
En la mayoría de los casos, no necesita esto:
- Si el valor que necesita se puede calcular completamente a partir de los accesorios actuales u otro estado, elimine ese estado redundante por completo. Si te preocupa volver a calcular con demasiada frecuencia, el Hook
useMemo
puede ayudarte. - Si desea restablecer el estado de todo el árbol de componentes, pase una
key
diferente a su componente. - Si puede, actualice todo el estado relevante en los controladores de eventos.
En el raro caso de que ninguno de estos se aplique, hay un patrón que puede usar para actualizar el estado en función de los valores que se han representado hasta el momento, llamando a una función set
mientras su componente se está procesando.
Aquí hay un ejemplo. Este componente EtiquetaDeConteo
muestra la propiedad conteo
que se le pasó:
export default function EtiquetaDeConteo({ conteo }) {
return <h1>{conteo}</h1>
}
Digamos que quiere mostrar si el contador ha aumentado o disminuido desde el último cambio. El accesorio conteo
no le dice esto, — necesita realizar un seguimiento de su valor anterior. Agregue la variable de estado prevConteo
para realizar un seguimiento. Agregue otra variable de estado llamada trend
para determinar si el conteo ha aumentado o disminuido. Compare prevConteo
con conteo
y, si no son iguales, actualice tanto prevConteo
como trend
. Ahora puede mostrar tanto el accesorio de conteo actual como cómo ha cambiado desde el último renderizado.
import { useState } from 'react'; export default function EtiquetaDeConteo({ conteo }) { const [prevConteo, setprevConteo] = useState(conteo); const [trend, setTrend] = useState(null); if (prevConteo !== conteo) { setprevConteo(conteo); setTrend(conteo > prevConteo ? 'incrementando' : 'disminuyendo'); } return ( <> <h1>{conteo}</h1> {trend && <p>El conteo está {trend}</p>} </> ); }
Tenga en cuenta que si llama a una función set
durante la renderización, debe estar dentro de una condición como prevConteo !== conteo
, y debe haber una llamada como setPrevConteo(conteo)
dentro de la condición. De lo contrario, su componente se volvería a procesar en un bucle hasta que se bloquee. Además, solo puede actualizar el estado del componente actualmente renderizado de esta manera. Llamar a la función set
de otro componente durante el renderizado es un error. Finalmente, su llamada set
aún debería actualizar el estado sin mutación — este caso especial no significa que pueda romper otras reglas de funciones puras.
Este patrón puede ser difícil de entender y, por lo general, es mejor evitarlo. Sin embargo, es mejor que actualizar el estado en un efecto. Cuando llamas a la función set
durante el renderizado, React volverá a renderizar ese componente inmediatamente después de que tu componente salga con una declaración return
y antes de renderizar a los elementos secundarios. De esta manera, sus hijos no necesitan renderizar dos veces. El resto de la función de su componente aún se ejecutará (y el resultado se descartará), pero si su condición está por debajo de todas las llamadas a Hooks, puede agregar un retorno
anticipado dentro de él para reiniciar el renderizado antes.
Reference
useState(initialState)
Llame a useState
en el nivel superior de su componente para declarar una variable de estado.
import { useState } from 'react';
function MyComponent() {
const [edad, setEdad] = useState(28);
const [nombre, setNombre] = useState('Taylor');
const [todos, setTodos] = useState(() => createTodos());
// ...
La convención es nombrar variables de estado como [algo, setAlgo]
usando desestructuración de matriz.
Parameteros
initialState
: El valor que desea que tenga el estado inicialmente. Puede ser un valor de cualquier tipo, pero hay un comportamiento especial para las funciones. Este argumento se ignora después del renderizado inicial.- Si pasa una función como
initialState
, se tratará como una función inicializadora. Debe ser pura, no debe aceptar argumentos y debe devolver un valor de cualquier tipo. React llamará a su función de inicialización al inicializar el componente y almacenará su valor de retorno como el estado inicial. Vea un ejemplo arriba.
- Si pasa una función como
Returns
useState
devuelve una matriz con exactamente dos valores:
- El estado actual. Durante el primer renderizado, coincidirá con el
initialState
que haya pasado. - La función
set
que le permite actualizar el estado a un valor diferente y desencadenar una nueva representación.
Advertencias
useState
es un Hook, por lo que solo puede llamarlo en el nivel superior de su componente o sus propios Hooks. No puedes llamarlo dentro de bucles o condiciones. Si lo necesita, extraiga un nuevo componente y mueva el estado a él.- En Modo estricto, React llamará a su función de inicialización dos veces para ayudarlo a encontrar impurezas accidentales. Este es un comportamiento exclusivo de desarrollo y no afecta la producción. Si su función de inicialización es pura (como debería ser), esto no debería afectar la lógica de su componente. Se ignorará el resultado de una de las llamadas.
Funciones set
, como setAlgo(siguienteEstado)
La función set
devuelta por useState
le permite actualizar el estado a un valor diferente y desencadenar una nueva representación. Puede pasar el siguiente estado directamente, o una función que lo calcule a partir del estado anterior:
const [nombre, setNombre] = useState('Edward');
function handleClick() {
setNombre('Taylor');
setEdad(a => a + 1);
// ...
Parametros
siguienteEstado
: El valor que desea que tenga el estado. Puede ser un valor de cualquier tipo, pero hay un comportamiento especial para las funciones.- Si pasa una función como
siguienteEstado
, se tratará como una función de actualización. Debe ser puro, debe tomar el estado pendiente como único argumento y debe devolver el siguiente estado. React pondrá su función de actualización en una cola y volverá a renderizar su componente. Durante el próximo renderizado, React calculará el siguiente estado aplicando todas las actualizaciones en cola al estado anterior. Vea un ejemplo arriba.
Returns
Las funciones set
no tienen un valor de retorno.
Advertencias
-
La función
set
solo actualiza la variable de estado para el próximo renderizado. Si lee la variable de estado después de llamar a la funciónset
, todavía obtendrá el valor anterior que estaba en la pantalla antes de su llamada. -
Si el nuevo valor que proporciona es idéntico al
estado
actual, según lo determinado por unObject.is
, React omitirá volver a renderizar el componente y sus elementos secundarios. Esta es una optimización. Aunque en algunos casos React aún puede necesitar llamar a su componente antes de omitir a los elementos secundarios, no debería afectar su código. -
Reaccionar actualizaciones de estado por lotes. Actualiza la pantalla después de que todos los controladores de eventos se hayan ejecutado y hayan llamado a sus funciones
set
. Esto evita múltiples renderizaciones durante un solo evento. En el raro caso de que necesite forzar a React a actualizar la pantalla antes, por ejemplo, para acceder al DOM, puede usarflushSync
. -
Llamar a la función
set
durante el renderizado solo está permitido desde el componente de renderizado actual. React descartará su salida e inmediatamente intentará renderizarla nuevamente con el nuevo estado. Este patrón rara vez se necesita, pero puede usarlo para almacenar información de los renderizados anteriores. Vea un ejemplo arriba. -
En Modo estricto, React llamará a su función de actualización dos veces para ayudarlo a encontrar impurezas accidentales. Este es un comportamiento exclusivo de desarrollo y no afecta la producción. Si su función de actualización es pura (como debería ser), esto no debería afectar la lógica de su componente. Se ignorará el resultado de una de las llamadas.
Solución de problemas
He actualizado el estado, pero el registro me da el valor anterior
Llamar a la función set
no cambia el estado en el código en ejecución:
function handleClick() {
console.log(conteo); // 0
setConteo(conteo + 1); // Solicitar un re-render con 1
console.log(conteo); // Todavía 0!
setTimeout(() => {
console.log(conteo); // Tambien es 0!
}, 5000);
}
Esto se debe a que los estados se comportan como una instantánea. La actualización del estado solicita otro procesamiento con el nuevo valor del estado, pero no afecta la variable de JavaScript conteo
en su evento Handler que ya se está ejecutando.
Si necesita usar el siguiente estado, puede guardarlo en una variable antes de pasarlo a la función set
:
const nextconteo = conteo + 1;
setConteo(nextconteo);
console.log(conteo); // 0
console.log(nextconteo); // 1
He actualizado el estado, pero la pantalla no se actualiza
React ignorará su actualización si el siguiente estado es igual al estado anterior, según lo determine un Object.is
. Esto generalmente sucede cuando cambia un objeto o una matriz en el estado directamente :
obj.x = 10; // 🚩 Incorrecto: mutar objeto existente
setObj(obj); // 🚩 No hace nada
Mutó un objeto obj
existente y lo devolvió a setObj
, por lo que React ignoró la actualización. Para solucionar esto, debe asegurarse de estar siempre reemplazando objetos y arreglos en estado en lugar de mutarlos :
// ✅ Correcto: creando un nuevo objeto
setObj({
...obj,
x: 10
});
Recibo un error: “Demasiados renderizados”
Es posible que reciba un error que diga: Demasiados renderizados. React limita la cantidad de renderizaciones para evitar un bucle infinito.
Por lo general, esto significa que está configurando incondicionalmente el estado durante el renderizado, por lo que su componente entra en un bucle: renderizar, establecer el estado (lo que provoca un renderizado), renderizar, establecer estado (que provoca un renderizado), y así sucesivamente. Muy a menudo, esto se debe a un error al especificar un controlador de eventos:
// 🚩 Incorrecto: llama al controlador durante el procesamiento
return <button onClick={handleClick()}>Haz click en mi</button>
// ✅ Correcto: pasa el controlador de eventos
return <button onClick={handleClick}>Haz click en mi</button>
// ✅ Correcto: transmite una función en línea
return <button onClick={(e) => handleClick(e)}>Haz click en mi</button>
Si no puede encontrar la causa de este error, haga clic en la flecha al lado del error en la consola y mire a través de la pila de JavaScript para encontrar la llamada de función set
específica responsable del error.
Mi función de inicializador o actualizador se ejecuta dos veces
En Modo estricto, React llamará a algunas de sus funciones dos veces en lugar de una:
function TodoList() {
// Esta función de componente se ejecutará dos veces por cada procesamiento.
const [todos, setTodos] = useState(() => {
// Esta función de inicialización se ejecutará dos veces durante la inicialización.
return crearTodos();
});
function handleClick() {
setTodos(prevTodos => {
// Esta función de actualización se ejecutará dos veces por cada click.
return [...prevTodos, crearTodo()];
});
}
// ...
Esto se espera y no debería romper su código.
Este comportamiento de solo desarrollo lo ayuda a mantener los componentes puros. React usa el resultado de una de las llamadas e ignora el resultado de la otra llamada. Siempre que sus funciones de componente, inicializador y actualizador sean puras, esto no debería afectar su lógica. Sin embargo, si son impuros accidentalmente, esto le ayuda a detectar los errores y corregirlos.
Por ejemplo, esta función de actualización impura muta una matriz en el estado:
setTodos(prevTodos => {
// 🚩 Error: estado mutando
prevTodos.push(crearTodo());
});
Debido a que React llama a su función de actualización dos veces, verá que la tarea pendiente se agregó dos veces, por lo que sabrá que hay un error. En este ejemplo, puede corregir el error reemplazando la matriz en lugar de mutarla:
setTodos(prevTodos => {
// ✅ Correcto: reemplazando con nuevo estado
return [...prevTodos, crearTodo()];
});
Ahora que esta función de actualización es pura, llamarla un tiempo extra no hace una diferencia en el comportamiento. Es por eso que React llamarlo dos veces lo ayuda a encontrar errores. Solo las funciones de componente, inicializador y actualizador deben ser puras. Los controladores de eventos no necesitan ser puros, por lo que React nunca llamará a sus controladores de eventos dos veces.
Lea manteniendo los componentes puros para obtener más información.
Estoy tratando de establecer el estado de una función, pero es llamado en su lugar
No puedes poner una función en un estado como este:
const [fn, setFn] = useState(algunaFuncion);
function handleClick() {
setFn(algunaOtraFuncion);
}
Debido a que estás pasando una función, React asume que algunaFuncion
es una función inicializadora, y que algunaOtraFuncion
es una función actualizadora, por lo que intenta llamarlos y almacenar el resultado. Para realmente almacenar una función, tienes que poner () =>
delante de ellos en ambos casos. Entonces React almacenará las funciones que pase.
const [fn, setFn] = useState(() => algunaFuncion);
function handleClick() {
setFn(() => algunaOtraFuncion);
}