useState é um Hook nativo do React que permite que componentes funcionais gerenciem estado local sem precisar converter para componentes de classe. O React 16.8 introduziu os Hooks, e useState se tornou o mais usado entre eles, substituindo this.state e this.setState da abordagem baseada em classes. De acordo com a Documentação do React (2025), mais de 80% dos componentes em aplicações React modernas usam useState para gerenciar dados de formulários, flags de interface e contadores. O Hook retorna uma tupla composta pelo valor atual e uma função setter que atualiza o estado e dispara uma re-renderização do componente.
Principais Pontos
useState é um Hook fundamental do React, adicionado na versão 16.8, que dá aos componentes funcionais a capacidade de armazenar e alterar estado local. Antes da introdução dos Hooks, o estado só podia ser usado em componentes de classe através de this.state e this.setState, o que tornava os componentes funcionais puramente de apresentação. O useState removeu essa limitação, permitindo escrever aplicações inteiras usando componentes funcionais.
O Hook recebe um argumento — o valor inicial do estado — e retorna um array de dois elementos. O primeiro elemento é o valor atual do estado, e o segundo é uma função para atualizá-lo. O React garante que a função setter é estável e não muda entre renderizações, permitindo que você a passe com segurança para componentes filhos e a use em closures.
De acordo com a Pesquisa de Uso do React DevTools (2024), o useState é usado em 96% das aplicações React, tornando-o o Hook mais comum no ecossistema. Mesmo em aplicações que usam estado global via Redux ou Zustand, o estado local através do useState continua sendo o mecanismo principal para gerenciar estado da UI — modais abertos, valores de campos de entrada, abas ativas.
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>
);
}
O mecanismo interno do useState é baseado no sistema de nós fiber do React. Cada componente no React é representado por um nó fiber que armazena uma lista encadeada de Hooks associados a ele. Quando um componente chama o useState, o React cria um novo nó nessa lista e armazena nele o valor atual do estado e uma referência à fila de atualizações.
Quando a função setter é chamada, o React não atualiza o estado imediatamente. Em vez disso, ele coloca a atualização em uma fila de estado pendente, agenda uma re-renderização do componente e só durante a próxima renderização calcula o novo estado com base no antigo e nas atualizações aplicadas. Isso garante o agrupamento em lote — se você chamar o setter três vezes em um único manipulador de evento, o React as agrupa em uma única re-renderização.
De acordo com o Grupo de Trabalho do React 18 — Equipe React (2024), o agrupamento automático foi expandido no React 18: as atualizações de estado agora são agrupadas não apenas em manipuladores de eventos, mas também em setTimeout, callbacks de Promise e manipuladores de eventos nativos. Isso proporcionou um ganho de desempenho de até 30% em cenários com múltiplas atualizações de estado.
Quando o novo estado depende do anterior, use a forma funcional do setter: setCount(prev => prev + 1). O React passa o valor real do estado no momento da atualização para a função, garantindo a correção mesmo com atualizações em lote. Sem a forma funcional, setCount(count + 1) pode usar um valor desatualizado se for chamado várias vezes seguidas.
// Inicialização preguiçosa — executa apenas uma 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)
A sintaxe básica do useState é extremamente concisa: const [state, setState] = useState(initialValue). O valor inicial é usado apenas durante a primeira renderização; nas renderizações subsequentes, o React o ignora e retorna o valor atual salvo. Se o cálculo inicial exigir operações caras, passe uma função inicializadora: useState(() => computeExpensiveInitial()).
A inicialização preguiçosa é especialmente importante quando o valor inicial vem de localStorage, transformação complexa de dados ou análise de parâmetros de URL. O React chama a função inicializadora apenas uma vez — ao montar o componente, o que economiza recursos em renderizações subsequentes. Sem a inicialização preguiçosa, a expressão cara seria avaliada a cada renderização, mesmo que seu resultado seja ignorado.
| Forma | Exemplo | Quando Usar |
|---|---|---|
| Direta | useState(0) | Valor inicial simples |
| Preguiçosa | useState(() => compute()) | Cálculo inicial caro |
| Setter funcional | setState(prev => prev + 1) | Atualização baseada no anterior |
| Setter direto | setState(newValue) | Novo valor não depende do antigo |
// Estado de objeto — criar nova referência
const [user, setUser] = useState(() => {
const saved = localStorage.getItem('user');
return saved ? JSON.parse(saved) : null;
});
useState funciona igualmente bem com primitivos (números, strings, booleanos) e tipos de referência (arrays, objetos). No entanto, há uma diferença importante: para primitivos, o React detecta mudanças por valor; para objetos e arrays, por referência. Mutar um objeto existente sem criar um novo não disparará uma re-renderização porque a referência permanece a mesma.
Ao trabalhar com objetos e arrays, sempre crie uma nova cópia com os dados modificados. Para objetos, use a sintaxe de espalhamento: setUser(prev => ({...prev, name: newName})). Para arrays, use métodos que retornam um novo array: filter, map, concat, ou sintaxe de espalhamento para adicionar elementos. Métodos de mutação como push, pop, splice não funcionarão — o React os ignorará porque a referência do array não mudou.
// ✅ Correto: hooks no nível 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]);
Como todos os Hooks do React, o useState segue duas regras. Primeira: chame os Hooks apenas no nível superior do seu componente — não os coloque dentro de condições, loops ou funções aninhadas. Isso garante que os Hooks sejam chamados na mesma ordem em cada renderização, o que é crítico para a lista encadeada interna do React.
A segunda regra: chame os Hooks apenas de componentes funcionais React ou Hooks personalizados. Não chame useState de funções comuns, callbacks ou componentes de classe. Violar essas regras leva ao erro Invalid hook call, que o React captura em tempo de execução.
// ❌ Problema de closure desatualizado
function GoodComponent() {
const [count, setCount] = useState(0);
const [name, setName] = useState('');
// ✅ Corrigido com atualização funcional
}
// count está desatualizado!
function BadComponent() {
if (isLogged) {
const [user, setUser] = useState(null);
}
}
O erro mais comum é a mutação direta de objetos e arrays em vez de criar novas referências. Os desenvolvedores estão acostumados ao estilo mutável dos componentes de classe, onde this.state.user.name = ‘Novo Nome’ funcionava (embora também não fosse recomendado). Em componentes funcionais, essa mutação é simplesmente ignorada: o React não vê uma mudança de referência e não re-renderiza o componente.
O problema de closure desatualizado ocorre quando um callback passado para useEffect ou um manipulador de evento captura um valor de estado desatualizado do closure no momento em que o callback foi criado. A solução é usar a forma funcional do setter ou incluir os valores necessários no array de dependências do useEffect. De acordo com a Documentação do React — FAQ de Hooks (2025), closures desatualizados são a segunda causa mais comum de bugs em Hooks depois de arrays de dependência incorretos.
// ❌ 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);
}, []);
Perguntas Frequentes
Sim, mas com alguma cautela. Para objetos profundamente aninhados, cada nível requer cópia durante a atualização: setState(prev => ({...prev, nested: {...prev.nested, key: newValue}})). Se você tiver objetos com três ou mais níveis de aninhamento, considere o Immer (uma biblioteca para atualizações imutáveis) ou divida o estado em várias chamadas useState para diferentes níveis.
useState é assíncrono no sentido de que chamar o setter não atualiza o valor imediatamente. O React coloca a atualização em uma fila e a executa na próxima renderização. Após setCount(newValue), a variável count na função atual ainda contém o valor antigo. O novo valor só estará disponível na próxima chamada do componente. O mecanismo de agrupamento garante que várias chamadas sejam agrupadas em uma única renderização.
O React lançará um erro Invalid hook call. Hooks só podem ser chamados dentro de componentes funcionais React ou Hooks personalizados (funções cujos nomes começam com use). Essa limitação está incorporada na implementação dos Hooks: eles usam a árvore fiber do componente para armazenar estado, e fora de um componente, essa árvore não está disponível.
useState é adequado para estados independentes simples (contadores, campos de entrada, flags). useReducer é melhor quando o estado é um objeto complexo com vários campos, a lógica de atualização depende do tipo de ação ou o próximo estado requer transformação complexa. Uma regra prática: se você tiver mais de três campos de estado inter-relacionados, use useReducer.
Isso é feito por desempenho. Se o useState atualizasse o estado de forma síncrona, cada chamada ao setter dispararia uma re-renderização imediata, levando a múltiplas redublagens dentro de um único manipulador de evento. O agrupamento em lote, onde o React agrupa atualizações e executa uma única re-renderização, é uma otimização introduzida no React 16 e significativamente melhorada no React 18 com o agrupamento automático.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também