useState es un React Hook que le permite agregar una variable de estado a su componente.

const [estado, setEstado] = useState(estadoInicial)

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:

  1. El estado actual de esta variable de estado, establecida inicialmente en el estado inicial que proporcionó.
  2. 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.

Atención

Llamando a la función set no cambia el estado actual en el código que ya se está ejecutando :

function handleClick() {
setNombre('Robin');
console.log(nombre); // Sigue siendo "Taylor"!
}

Solo afecta lo que useState devolverá a partir del siguiente render.

Prueba algunos ejemplos

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:

  1. e => e + 1 recibirá 42 como estado pendiente y devolverá 43 como el siguiente estado.
  2. e => e + 1 recibirá 43 como estado pendiente y devolverá 44 como el siguiente estado.
  3. 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.

Prueba algunos ejemplos

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.

Prueba algunos ejemplos

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.

Prueba algunos ejemplos

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:

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.

Vea más ejemplos arriba.

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.

Returns

useState devuelve una matriz con exactamente dos valores:

  1. El estado actual. Durante el primer renderizado, coincidirá con el initialState que haya pasado.
  2. 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ón set, 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 un Object.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 usar flushSync.

  • 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);
}