useState: o que é, o hook de estado e sintaxe no React

Autor: IT Sectr Publicado: 2026-07-04 Tempo de leitura: 9 min

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 para gerenciar estado local em componentes funcionais React.
  • Tupla — retorna um par [valor, setter], onde setter é uma função de atualização de estado.
  • Re-renderização — chamar o setter sempre dispara uma re-renderização do componente com o novo valor.
  • Tipos de Dados — useState funciona com números, strings, arrays, objetos e valores booleanos.
  • Imutabilidade — o estado não pode ser mutado diretamente, apenas através da função setter.

O que é useState no React

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.

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

Como o useState funciona

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.

Atualização Funcional 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.

jsx
// 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)

Sintaxe do useState: Declaração e Atualização

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.

FormaExemploQuando Usar
DiretauseState(0)Valor inicial simples
PreguiçosauseState(() => compute())Cálculo inicial caro
Setter funcionalsetState(prev => prev + 1)Atualização baseada no anterior
Setter diretosetState(newValue)Novo valor não depende do antigo
jsx
// Estado de objeto — criar nova referência
const [user, setUser] = useState(() => {
    const saved = localStorage.getItem('user');
    return saved ? JSON.parse(saved) : null;
});

useState com Diferentes Tipos de Dados

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.

jsx
// ✅ 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]);

Regras para Uso de Hooks com useState

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.

  • Não em condições — if (flag) { useState(0) } viola a ordem das chamadas de Hook.
  • Não em loops — for (...) { useState(0) } torna a ordem imprevisível.
  • Não em callbacks — useEffect(() => { useState(0) }, []) quebra a lista encadeada.
jsx
// ❌ 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);
    }
}

Erros Comuns com useState

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.

  • Mutação de objeto — user.name = ‘Novo’ em vez de setUser({...user, name: ‘Novo’}).
  • Closure desatualizado — usar um valor obsoleto no setter sem a forma funcional.
  • Re-renderizações extras — chamar o setter com o mesmo valor (React 18 ainda renderiza para primitivos).
  • Redefinição de estado — atualizar involuntariamente a prop key de um componente, fazendo com que ele monte novamente.

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.

jsx
// ❌ 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

O useState pode ser usado para objetos aninhados complexos?

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.

O useState é assíncrono ou síncrono?

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 que acontece se useState for chamado fora de um componente?

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.

Quando usar useState e quando usar useReducer?

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.

Por que o useState não atualiza o valor imediatamente?

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

  • useState — um Hook básico do React para gerenciar estado local em componentes funcionais, retornando [state, setter].
  • Imutabilidade — o estado não pode ser mutado diretamente; sempre passe um novo valor ou use a forma funcional do setter.
  • Inicialização preguiçosa — para cálculos iniciais caros, passe uma função: useState(() => compute()).
  • Setter funcional — setState(prev => prev + 1) garante correção com atualizações em lote.
  • Agrupamento em lote — o React agrupa várias chamadas ao setter em uma única re-renderização para otimização.
  • Regras de Hooks — chame useState apenas no nível superior do componente, não em condições ou loops.
  • Tipos de dados — para objetos e arrays, sempre crie uma nova referência ao atualizar.

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.

Discutir o projeto

Leia também