useState é um Hook do React que permite adicionar uma variável de state ao seu componente.

const [state, setState] = useState(initialState)

Referência

useState(initialState)

Chame useState no nível superior do seu componente para declarar uma variável de state.

import { useState } from 'react';

function MeuComponente() {
const [idade, setIdade] = useState(28);
const [nome, setNome] = useState('Taylor');
const [tarefas, setTarefas] = useState(() => criarTarefas());
// ...

A convenção é nomear variáveis de state como [algo, setAlgo] usando desestruturação de array.

Veja mais exemplos abaixo.

Parâmetros

  • initialState: O valor que você quer que o state tenha inicialmente. Pode ser um valor de qualquer tipo, mas há um comportamento especial para funções. Este argumento é ignorado após a renderização inicial.
    • Se você passar uma função como initialState, ela será tratada como uma função inicializadora. Ela deve ser pura, não deve receber argumentos e deve retornar um valor de qualquer tipo. O React chamará sua função inicializadora ao inicializar o componente e armazenará seu valor de retorno como o state inicial. Veja um exemplo abaixo.

Retornos

useState retorna um array com exatamente dois valores:

  1. O state atual. Durante a primeira renderização, ele corresponderá ao initialState que você passou.
  2. A função set que permite atualizar o state para um valor diferente e acionar uma nova renderização.

Ressalvas

  • useState é um Hook, então você só pode chamá-lo no nível superior do seu componente ou em seus próprios Hooks. Você não pode chamá-lo dentro de loops ou condições. Se precisar disso, extraia um novo componente e mova o state para ele.
  • No Modo Estrito, o React chamará sua função inicializadora duas vezes para ajudar você a encontrar impurezas acidentais. Este é um comportamento apenas de desenvolvimento e não afeta a produção. Se sua função inicializadora for pura (como deveria ser), isso não afetará o comportamento. O resultado de uma das chamadas será ignorado.

Funções set, como setAlgo(proximoEstado)

A função set retornada por useState permite atualizar o state para um valor diferente e acionar uma nova renderização. Você pode passar o próximo state diretamente ou uma função que o calcule a partir do state anterior:

const [nome, setNome] = useState('Edward');

function handleClick() {
setNome('Taylor');
setIdade(i => i + 1);
// ...

Parâmetros

  • proximoEstado: O valor que você quer que o state seja. Pode ser um valor de qualquer tipo, mas há um comportamento especial para funções.
    • Se você passar uma função como proximoEstado, ela será tratada como uma função atualizadora. Ela deve ser pura, deve receber o state pendente como seu único argumento e deve retornar o próximo state. O React colocará sua função atualizadora em uma fila e renderizará novamente seu componente. Durante a próxima renderização, o React calculará o próximo state aplicando todas as atualizações enfileiradas ao state anterior. Veja um exemplo abaixo.

Retornos

As funções set não têm valor de retorno.

Ressalvas

  • A função set apenas atualiza a variável de state para a próxima renderização. Se você ler a variável de state após chamar a função set, você ainda obterá o valor antigo que estava na tela antes da sua chamada.

  • Se o novo valor que você fornecer for idêntico ao state atual, determinado por uma comparação Object.is, o React ignorará a renderização do componente e seus filhos. Esta é uma otimização. Embora em alguns casos o React ainda possa precisar chamar seu componente antes de ignorar os filhos, isso não deve afetar seu código.

  • O React agrupa atualizações de state. Ele atualiza a tela depois que todos os manipuladores de eventos foram executados e chamaram suas funções set. Isso evita múltiplas renderizações durante um único evento. No raro caso em que você precise forçar o React a atualizar a tela mais cedo, por exemplo, para acessar o DOM, você pode usar flushSync.

  • Chamar a função set durante a renderização só é permitido dentro do componente atualmente em renderização. O React descartará sua saída e tentará imediatamente renderizá-lo novamente com o novo state. Este padrão raramente é necessário, mas você pode usá-lo para armazenar informações das renderizações anteriores. Veja um exemplo abaixo.

  • No Modo Estrito, o React chamará sua função atualizadora duas vezes para ajudar você a encontrar impurezas acidentais. Este é um comportamento apenas de desenvolvimento e não afeta a produção. Se sua função atualizadora for pura (como deveria ser), isso não afetará o comportamento. O resultado de uma das chamadas será ignorado.


Uso

Adicionando state a um componente

Chame useState no nível superior do seu componente para declarar uma ou mais variáveis de state.

import { useState } from 'react';

function MeuComponente() {
const [idade, setIdade] = useState(42);
const [nome, setNome] = useState('Taylor');
// ...

A convenção é nomear variáveis de state como [algo, setAlgo] usando desestruturação de array.

useState retorna um array com exatamente dois itens:

  1. O state atual desta variável de state, inicialmente definido como o state inicial que você forneceu.
  2. A função set que permite alterá-lo para qualquer outro valor em resposta à interação.

Para atualizar o que está na tela, chame a função set com algum state seguinte:

function handleClick() {
setNome('Robin');
}

O React armazenará o próximo state, renderizará seu componente novamente com os novos valores e atualizará a UI.

Pitfall

Chamar a função set não altera o state atual no código já em execução:

function handleClick() {
setNome('Robin');
console.log(nome); // Ainda "Taylor"!
}

Isso apenas afeta o que useState retornará a partir da próxima renderização.

Exemplos básicos de useState

Example 1 of 4:
Contador (número)

Neste exemplo, a variável de state count contém um número. Clicar no botão o incrementa.

import { useState } from 'react';

export default function Contador() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <button onClick={handleClick}>
      Você clicou {count} vezes
    </button>
  );
}


Atualizando o state com base no state anterior

Suponha que idade seja 42. Este manipulador chama setIdade(idade + 1) três vezes:

function handleClick() {
setIdade(idade + 1); // setIdade(42 + 1)
setIdade(idade + 1); // setIdade(42 + 1)
setIdade(idade + 1); // setIdade(42 + 1)
}

No entanto, após um clique, idade será apenas 43 em vez de 45! Isso ocorre porque chamar a função set não atualiza a variável de state idade no código já em execução. Então, cada chamada setIdade(idade + 1) se torna setIdade(43).

Para resolver esse problema, você pode passar uma função atualizadora para setIdade em vez do próximo state:

function handleClick() {
setIdade(a => a + 1); // setIdade(42 => 43)
setIdade(a => a + 1); // setIdade(43 => 44)
setIdade(a => a + 1); // setIdade(44 => 45)
}

Aqui, a => a + 1 é sua função atualizadora. Ela recebe o state pendente e calcula o próximo state a partir dele.

O React coloca suas funções atualizadoras em uma fila. Em seguida, durante a próxima renderização, ele as chamará na mesma ordem:

  1. a => a + 1 receberá 42 como o state pendente e retornará 43 como o próximo state.
  2. a => a + 1 receberá 43 como o state pendente e retornará 44 como o próximo state.
  3. a => a + 1 receberá 44 como o state pendente e retornará 45 como o próximo state.

Não há outras atualizações enfileiradas, então o React armazenará 45 como o state atual no final.

Por convenção, é comum nomear o argumento do state pendente com a primeira letra do nome da variável de state, como a para idade. No entanto, você também pode chamá-lo de idadeAnterior ou algo que considere mais claro.

O React pode chamar suas atualizações duas vezes no desenvolvimento para verificar se são puras.

Deep Dive

Usar sempre uma função atualizadora é preferível?

Você pode ouvir uma recomendação para sempre escrever código como setIdade(a => a + 1) se o state que você está definindo for calculado a partir do state anterior. Não há problema nisso, mas nem sempre é necessário.

Na maioria dos casos, não há diferença entre essas duas abordagens. O React sempre garante que para ações intencionais do usuário, como cliques, a variável de state idade seria atualizada antes do próximo clique. Isso significa que não há risco de um manipulador de cliques ver um valor “obsoleto” de idade no início do manipulador de eventos.

No entanto, se você fizer várias atualizações dentro do mesmo evento, as funções atualizadoras podem ser úteis. Elas também são úteis se acessar a própria variável de state for inconveniente (você pode encontrar isso ao otimizar re-renderizações).

Se você preferir consistência em vez de uma sintaxe um pouco mais verbosa, é razoável sempre escrever uma função atualizadora se o state que você está definindo for calculado a partir do state anterior. Se for calculado a partir do state anterior de alguma outra variável de state, você pode querer combiná-las em um objeto e usar um reducer.

A diferença entre passar uma função atualizadora e passar o próximo state diretamente

Example 1 of 2:
Passando a função atualizadora

Este exemplo passa a função atualizadora, então o botão “+3” funciona.

import { useState } from 'react';

export default function Contador() {
  const [idade, setIdade] = useState(42);

  function incrementar() {
    setIdade(a => a + 1);
  }

  return (
    <>
      <h1>Sua idade: {idade}</h1>
      <button onClick={() => {
        incrementar();
        incrementar();
        incrementar();
      }}>+3</button>
      <button onClick={() => {
        incrementar();
      }}>+1</button>
    </>
  );
}


Atualizando objetos e arrays no state

Você pode colocar objetos e arrays no state. No React, o state é considerado somente leitura, então você deve substituí-lo em vez de mutar seus objetos existentes. Por exemplo, se você tem um objeto form no state, não o mute:

// 🚩 Não mute um objeto no state desta forma:
form.firstName = 'Taylor';

Em vez disso, substitua o objeto inteiro criando um novo:

// ✅ Substitua o state com um novo objeto
setForm({
...form,
firstName: 'Taylor'
});

Leia atualizando objetos no state e atualizando arrays no state para saber mais.

Exemplos de objetos e arrays no state

Example 1 of 4:
Formulário (objeto)

Neste exemplo, a variável de state form contém um objeto. Cada entrada tem um manipulador de alteração que chama setForm com o próximo state de todo o formulário. A sintaxe de propagação { ...form } garante que o objeto de state seja substituído em vez de mutado.

import { useState } from 'react';

export default function Form() {
  const [form, setForm] = useState({
    firstName: 'Barbara',
    lastName: 'Hepworth',
    email: 'bhepworth@sculpture.com',
  });

  return (
    <>
      <label>
        Primeiro nome:
        <input
          value={form.firstName}
          onChange={e => {
            setForm({
              ...form,
              firstName: e.target.value
            });
          }}
        />
      </label>
      <label>
        Sobrenome:
        <input
          value={form.lastName}
          onChange={e => {
            setForm({
              ...form,
              lastName: e.target.value
            });
          }}
        />
      </label>
      <label>
        Email:
        <input
          value={form.email}
          onChange={e => {
            setForm({
              ...form,
              email: e.target.value
            });
          }}
        />
      </label>
      <p>
        {form.firstName}{' '}
        {form.lastName}{' '}
        ({form.email})
      </p>
    </>
  );
}


Evitando recriar o state inicial

O React salva o state inicial uma vez e ignora-o nas renderizações subsequentes.

function TodoList() {
const [todos, setTodos] = useState(createInitialTodos());
// ...

Embora o resultado de createInitialTodos() seja usado apenas para a renderização inicial, você ainda está chamando essa função em toda renderização. Isso pode ser dispendioso se estiver criando grandes arrays ou realizando cálculos caros.

Para resolver isso, você pode passar uma função inicializadora para useState:

function TodoList() {
const [todos, setTodos] = useState(createInitialTodos);
// ...

Note que você está passando createInitialTodos, que é a função em si, e não createInitialTodos(), que é o resultado de chamá-la. Se você passar uma função para useState, o React só a chamará durante a inicialização.

O React pode chamar seus inicializadores duas vezes no modo de desenvolvimento para verificar se eles são puros.

A diferença entre passar uma função inicializadora e passar o state inicial diretamente

Example 1 of 2:
Passando a função inicializadora

Este exemplo passa a função inicializadora, então a função createInitialTodos só é executada durante a inicialização. Ela não é executada quando o componente é re-renderizado, como quando você digita na entrada.

import { useState } from 'react';

function createInitialTodos() {
  const initialTodos = [];
  for (let i = 0; i < 50; i++) {
    initialTodos.push({
      id: i,
      text: 'Item ' + (i + 1)
    });
  }
  return initialTodos;
}

export default function TodoList() {
  const [todos, setTodos] = useState(createInitialTodos);
  const [text, setText] = useState('');

  return (
    <>
      <input
        value={text}
        onChange={e => setText(e.target.value)}
      />
      <button onClick={() => {
        setText('');
        setTodos([{
          id: todos.length,
          text: text
        }, ...todos]);
      }}>Adicionar</button>
      <ul>
        {todos.map(item => (
          <li key={item.id}>
            {item.text}
          </li>
        ))}
      </ul>
    </>
  );
}


Reiniciando o state com uma chave

Você frequentemente encontrará o atributo key ao renderizar listas. No entanto, ele também serve a outro propósito.

Você pode reiniciar o state de um componente passando uma key diferente para um componente. Neste exemplo, o botão Reset altera a variável de state version, que passamos como uma key para o Form. Quando a key muda, o React recria o componente Form (e todos os seus filhos) do zero, então seu state é reiniciado.

Leia preservando e reiniciando o state para saber mais.

import { useState } from 'react';

export default function App() {
  const [version, setVersion] = useState(0);

  function handleReset() {
    setVersion(version + 1);
  }

  return (
    <>
      <button onClick={handleReset}>Reiniciar</button>
      <Form key={version} />
    </>
  );
}

function Form() {
  const [name, setName] = useState('Taylor');

  return (
    <>
      <input
        value={name}
        onChange={e => setName(e.target.value)}
      />
      <p>Olá, {name}.</p>
    </>
  );
}


Armazenando informações de renderizações anteriores

Normalmente, você atualizará o state em manipuladores de eventos. No entanto, em casos raros, você pode querer ajustar o state em resposta à renderização — por exemplo, você pode querer alterar uma variável de state quando uma prop muda.

Na maioria dos casos, você não precisa disso:

No raro caso em que nenhuma dessas se aplica, há um padrão que você pode usar para atualizar o state com base nos valores que foram renderizados até agora, chamando uma função set enquanto seu componente está renderizando.

Aqui está um exemplo. Este componente CountLabel exibe a prop count passada a ele:

export default function CountLabel({ count }) {
return <h1>{count}</h1>
}

Digamos que você queira mostrar se o contador aumentou ou diminuiu desde a última alteração. A prop count não lhe diz isso — você precisa manter o controle de seu valor anterior. Adicione a variável de state prevCount para rastreá-la. Adicione outra variável de state chamada trend para manter se o count aumentou ou diminuiu. Compare prevCount com count, e se não forem iguais, atualize tanto prevCount quanto trend. Agora você pode mostrar tanto a prop count atual quanto como ela mudou desde a última renderização.

import { useState } from 'react';

export default function CountLabel({ count }) {
  const [prevCount, setPrevCount] = useState(count);
  const [trend, setTrend] = useState(null);
  if (prevCount !== count) {
    setPrevCount(count);
    setTrend(count > prevCount ? 'aumentando' : 'diminuindo');
  }
  return (
    <>
      <h1>{count}</h1>
      {trend && <p>O contador está {trend}</p>}
    </>
  );
}

Note que se você chamar uma função set durante a renderização, ela deve estar dentro de uma condição como prevCount !== count, e deve haver uma chamada como setPrevCount(count) dentro da condição. Caso contrário, seu componente renderizaria em um loop até travar. Além disso, você só pode atualizar o state do componente atualmente renderizando desta forma. Chamar a função set de outro componente durante a renderização é um erro. Finalmente, sua chamada set ainda deve atualizar o state sem mutação — isso não significa que você pode quebrar outras regras das funções puras.

Este padrão pode ser difícil de entender e geralmente é melhor evitá-lo. No entanto, é melhor do que atualizar o state em um efeito. Quando você chama a função set durante a renderização, o React renderizará esse componente imediatamente após seu componente sair com uma instrução return, e antes de renderizar os filhos. Desta forma, os filhos não precisam renderizar duas vezes. O resto da função do seu componente ainda será executado (e o resultado será descartado). Se sua condição estiver abaixo de todas as chamadas de Hook, você pode adicionar um return; antecipado para reiniciar a renderização mais cedo.


Solução de problemas

Atualizei o state, mas o log me dá o valor antigo

Chamar a função set não altera o state no código em execução:

function handleClick() {
console.log(count); // 0

setCount(count + 1); // Solicita uma nova renderização com 1
console.log(count); // Ainda 0!

setTimeout(() => {
console.log(count); // Também 0!
}, 5000);
}

Isso ocorre porque o state se comporta como um snapshot. Atualizar o state solicita outra renderização com o novo valor de state, mas não afeta a variável JavaScript count no seu manipulador de eventos já em execução.

Se você precisa usar o próximo state, você pode salvá-lo em uma variável antes de passá-lo para a função set:

const nextCount = count + 1;
setCount(nextCount);

console.log(count); // 0
console.log(nextCount); // 1

Atualizei o state, mas a tela não atualiza

O React ignorará sua atualização se o próximo state for igual ao state anterior, determinado por uma comparação Object.is. Isso geralmente acontece quando você altera um objeto ou um array no state diretamente:

obj.x = 10; // 🚩 Errado: mutando um objeto existente
setObj(obj); // 🚩 Não faz nada

Você mutou um objeto obj existente e o passou de volta para setObj, então o React ignorou a atualização. Para corrigir isso, você precisa garantir que sempre está substituindo objetos e arrays no state em vez de mutá-los:

// ✅ Correto: criando um novo objeto
setObj({
...obj,
x: 10
});

Estou recebendo um erro: “Too many re-renders”

Você pode receber um erro que diz: Too many re-renders. React limits the number of renders to prevent an infinite loop. Tipicamente, isso significa que você está definindo o state incondicionalmente durante a renderização, então o componente entra em um loop: renderiza, define o state (o que causa uma renderização), renderiza, define o state (o que causa uma renderização), e assim por diante. Muito frequentemente, isso é causado por um erro na especificação de um manipulador de eventos:

// 🚩 Errado: chama o manipulador durante a renderização
return <button onClick={handleClick()}>Clique aqui</button>

// ✅ Correto: passa o manipulador de eventos
return <button onClick={handleClick}>Clique aqui</button>

// ✅ Correto: passa uma função inline
return <button onClick={(e) => handleClick(e)}>Clique aqui</button>

Se você não consegue encontrar a causa deste erro, clique na seta ao lado do erro no console e olhe através da pilha JavaScript para encontrar a chamada específica da função set responsável pelo erro.


Minha função inicializadora ou atualizadora é executada duas vezes

No Modo Estrito, o React chamará algumas de suas funções duas vezes em vez de uma:

function TodoList() {
// Esta função de componente será executada duas vezes para cada renderização.

const [todos, setTodos] = useState(() => {
// Esta função inicializadora será executada duas vezes durante a inicialização.
return createTodos();
});

function handleClick() {
setTodos(prevTodos => {
// Esta função atualizadora será executada duas vezes para cada clique.
return [...prevTodos, createTodo()];
});
}
// ...

Isso é esperado e não deve quebrar seu código.

Este comportamento apenas de desenvolvimento ajuda você a manter os componentes puros. O React usa o resultado de uma das chamadas e ignora o resultado da outra chamada. Desde que suas funções de componente, inicializadoras e atualizadoras sejam puras, isso não deve afetar sua lógica. No entanto, se elas forem acidentalmente impuras, isso ajuda você a perceber os erros.

Por exemplo, esta função atualizadora impura muta um array no state:

setTodos(prevTodos => {
// 🚩 Erro: mutando o state
prevTodos.push(createTodo());
});

Como o React chama sua função atualizadora duas vezes, você verá que o todo foi adicionado duas vezes, então você saberá que há um erro. Neste exemplo, você pode corrigir o erro substituindo o array em vez de mutá-lo:

setTodos(prevTodos => {
// ✅ Correto: substituindo por um novo state
return [...prevTodos, createTodo()];
});

Agora que esta função atualizadora é pura, chamá-la uma vez extra não faz diferença no comportamento. É por isso que o React chamar duas vezes ajuda você a encontrar erros. Apenas funções de componente, inicializadoras e atualizadoras precisam ser puras. Manipuladores de eventos não precisam ser puros, então o React nunca chamará seus manipuladores de eventos duas vezes.

Leia mantendo componentes puros para saber mais.


Estou tentando definir o state para uma função, mas ela é chamada em vez disso

Você não pode colocar uma função no state assim:

const [fn, setFn] = useState(someFunction);

function handleClick() {
setFn(someOtherFunction);
}

Porque você está passando uma função, o React assume que someFunction é uma função inicializadora, e que someOtherFunction é uma função atualizadora, então tenta chamá-las e armazenar o resultado. Para realmente armazenar uma função, você precisa colocar () => antes delas em ambos os casos. Então o React armazenará as funções que você passa.

const [fn, setFn] = useState(() => someFunction);

function handleClick() {
setFn(() => someOtherFunction);
}