Gerenciamento de Estado em React: Qual Abordagem Escolher?
O que é gerenciamento de estado em React
Gerenciamento de estado é a forma como uma aplicação guarda, atualiza e distribui os dados que mudam ao longo do tempo: o que o usuário digitou, se um modal está aberto, a lista que veio da API. Em React, cada abordagem de gerenciamento de estado resolve um problema diferente, e usar a errada custa em complexidade ou em performance.
O erro mais comum é usar a abordagem errada para o tamanho do problema: aplicar uma
solução global a um caso local, forçar uma biblioteca pesada onde dois useState
resolveriam, ou sustentar o estado de um sistema inteiro em Context API e sofrer com
re-render em cascata.
Este artigo cobre quando cada abordagem de gerenciamento de estado em React faz sentido e como decidir sem depender de modismo.
Quando o useState resolve e quando ele começa a doer
useState resolve bem estado local: um campo de formulário, um contador, um accordion
aberto, um toggle de tema escuro. Ele começa a doer quando o mesmo dado precisa
aparecer em componentes que não têm relação direta de pai e filho.
O sintoma é sempre o mesmo: prop drilling. Um dado sobe até um ancestral comum e desce de novo por três, quatro, cinco níveis de componentes que não usam esse dado, só repassam. Cada componente no meio vira acoplamento desnecessário: mudar a assinatura de uma prop no topo obriga a mexer em todo o caminho até o consumidor final.
function Pagina() {
const [usuario, setUsuario] = useState(null);
return <Layout usuario={usuario} />;
}
function Layout({ usuario }) {
return <Sidebar usuario={usuario} />;
}
function Sidebar({ usuario }) {
return <Avatar usuario={usuario} />;
}
Layout e Sidebar não usam usuario para nada além de repassar adiante. Esse é o
ponto em que vale considerar outra abordagem, não antes disso.
Context API resolve prop drilling, não substitui um gerenciador global
Context API é um mecanismo do React para passar dado através da árvore de componentes sem repassar prop manualmente em cada nível. Ele resolve prop drilling, mas não foi desenhado para estado que muda com frequência alta.
O problema de performance é direto: todo componente que consome um Context re-renderiza
quando o valor do Context muda, mesmo que use só uma fatia pequena daquele valor. Um
Context com { usuario, tema, carrinho, notificacoes } faz o componente que só lê
tema re-renderizar toda vez que o carrinho muda.
A prática que evita isso é separar Contexts por domínio, um para cada fatia de estado que muda em ritmo diferente, em vez de manter um Context único para a aplicação inteira. Ainda assim, para estado que atualiza a cada tecla digitada ou a cada scroll, Context tende a custar caro demais em re-renders.
Quando trocar Context por Zustand, Jotai ou Redux
Bibliotecas externas de gerenciamento de estado resolvem o problema de re-render do Context: o componente só re-renderiza quando o pedaço específico de estado que ele lê muda, não quando qualquer parte do estado muda.
Zustand e Jotai compartilham essa filosofia de assinatura seletiva, com sintaxe mais enxuta que Redux. Zustand usa um hook único por store, sem Provider obrigatório:
import { create } from 'zustand';
const useCarrinho = create((set) => ({
itens: [],
adicionar: (item) => set((state) => ({ itens: [...state.itens, item] })),
}));
Redux continua fazendo sentido em times grandes que já têm a estrutura montada em volta dele: Redux DevTools para depurar cada ação disparada, middleware para lidar com efeito colateral, um padrão único que todo desenvolvedor do time já conhece de outros projetos. Para um projeto novo sem esse histórico de time, a complexidade extra do Redux raramente se paga.
Como decidir a abordagem certa para o seu projeto
Siga esta ordem antes de adicionar qualquer biblioteca de gerenciamento de estado ao projeto:
- Pergunte se o estado é usado por um componente só. Se sim,
useStateouuseReducerresolvem e nada além disso é necessário. - Pergunte se o estado é compartilhado, mas muda pouco: tema, idioma, dados do usuário logado. Context API resolve sem custo relevante de performance.
- Meça o re-render antes de trocar de abordagem. O React DevTools Profiler mostra quais componentes re-renderizam e por quê. Sem esse dado, trocar de Context para Zustand é chute.
- Se o Profiler confirmar re-render em cascata em estado que muda com frequência, migre a fatia específica de estado, não o projeto inteiro, para Zustand ou Jotai.
- Considere Redux só se o time já tem esse padrão consolidado em outros projetos, ou se o app precisa de rastreamento de ação para auditoria.
Pular a etapa de medir é o erro mais caro de todos. Trocar de biblioteca sem confirmar que o gargalo é gerenciamento de estado, quando o problema real está em outro lugar, como uma imagem sem otimização (veja como otimizar o LCP do site), custa tempo de reescrita sem resolver o sintoma original.
Erros comuns que pioram a performance do estado em React
O erro mais frequente é colocar estado que muda a cada tecla, como o valor de um input de busca, direto num Context ou numa store global. Isso propaga re-render para todo consumidor a cada caractere digitado, mesmo quando só o próprio input precisa daquele valor.
Outro erro é normalizar estado demais cedo. Duplicar um dado da API em três stores diferentes, para garantir acesso fácil de qualquer componente, cria divergência: a store A atualiza e a store B fica com o valor antigo, e ninguém lembra qual é a fonte de verdade.
Memoização mal aplicada também esconde o problema em vez de resolver. Envolver tudo
em useMemo e useCallback sem medir onde o re-render realmente incomoda adiciona
complexidade sem ganho, porque comparar dependências a cada renderização também tem
custo.
Se seu projeto React já mostra sinais de re-render em cascata ou o time perde tempo depurando qual store tem a verdade sobre um dado, fale com a gente para uma revisão de arquitetura.