- Entenda como o useState preserva e atualiza o estado local do componente, incluindo atualizações funcionais e manipulação de objetos.
- Use useEffect para efeitos colaterais com lógica de configuração/limpeza clara e arrays de dependência precisos para evitar vazamentos e loops.
- Combine useState e useEffect para tarefas do mundo real, como busca de dados, inscrições e atualizações do DOM em componentes funcionais.
- Siga as regras dos hooks e trate os efeitos como processos "após a renderização" para manter os componentes React previsíveis e fáceis de manter.

Os hooks do React mudaram completamente a forma como escrevemos componentes.e masterização useState e useEffect Basicamente, é o ingresso para escrever código React moderno. Se você já usa React, mas ainda se depara com loops infinitos, estado obsoleto ou arrays de dependências confusos, este guia ajudará você a conectar todas as peças que faltam de forma prática.
Neste artigo, vamos analisar em detalhes como usar corretamente useState e useEffect juntos, por que os hooks foram introduzidos em primeiro lugar, as regras e ressalvas oficiais, como as dependências realmente funcionam internamente, armadilhas comuns que quebram seus componentes e padrões testados e comprovados para efeitos colaterais, limpeza e gerenciamento de estado em projetos reais.
Por que usar hooks, e por que especificamente useState e useEffect?
Os Hooks foram adicionados no React 16.8 para permitir que componentes funcionais utilizassem recursos de estado e ciclo de vida sem classes.Antes disso, era necessário escrever componentes de classe para manter o estado local, inscrever-se em dados externos ou reagir a eventos do ciclo de vida, como montagem e desmontagem.
O grande problema com as classes era que a lógica relacionada frequentemente ficava dividida em vários métodos de ciclo de vida. como componentDidMount, componentDidUpdate e componentWillUnmountVocê acabaria com partes da mesma funcionalidade espalhadas por diferentes métodos baseados em quando eles fogem em vez de o que Sim, isso torna o código mais difícil de ler, testar e reutilizar.
Ganchos viram este modelo: com useState Você associa o estado diretamente a um componente de função, e com useEffect Você associa efeitos colaterais diretamente à lógica que precisa deles. Dessa forma, você pode agrupar tudo relacionado a uma única preocupação em um só lugar e extrair facilmente os ganchos reutilizáveis posteriormente.
Entre todos os ganchos, useState e useEffect são os elementos primitivos fundamentaisVocê pode criar a maioria das funcionalidades do dia a dia apenas com estes dois: estado da interface do usuário, como formulários e botões de alternância, requisições de rede, assinaturas, temporizadores, atualizações do DOM e muito mais. Outros hooks (useRef, useReducer, useContext, useMemo…) são ótimos, mas se baseiam nas mesmas ideias.
Regras dos hooks do React que você nunca deve quebrar.
Os hooks do React vêm com algumas regras rígidas que garantem seu funcionamento confiável em todas as renderizações.Se você as violar, verá erros de tempo de execução ou bugs muito sutis e difíceis de depurar.
Primeira regra: utilize hooks apenas dentro de componentes funcionais do React ou hooks personalizados.Você não pode usar useState or useEffect Em componentes de classe, funções utilitárias comuns ou fora de qualquer componente. Um padrão como este é inválido:
import React, { Component, useState } from 'react';
class App extends Component {
// ❌ This will throw - hooks don’t work in classes
const = useState(0);
render() {
return <h1>Hello, I am a Class Component!</h1>;
}
}
A abordagem correta é migrar para um componente funcional se você quiser usar hooks.:
import React, { useState } from 'react';
function App() {
const = useState('');
return (
<div>
Your JSX code goes in here...
</div>
);
}
export default App;
Segunda regra: chame os hooks apenas no nível superior do seu componente.Isso significa que não é permitido usar hooks dentro de loops, condições ou funções aninhadas. O React depende da chamada de hooks na mesma ordem em cada renderização para "corresponder" a cada evento. useState e useEffect A chamada utiliza os dados armazenados, portanto, é inválida:
function BadComponent({ enabled }) {
if (enabled) {
// ❌ Wrong: hook inside a conditional
const = useState(0);
}
// ...
}
Em vez disso, declare os hooks incondicionalmente no início e use condicionais dentro do efeito ou JSX.O gancho (hook) deve sempre ser chamado, mas a lógica que ele executa pode ser condicional:
function ConditionalEffectComponent() {
const = useState(false);
useEffect(() => {
if (isMounted) {
console.log('Component mounted');
}
}, );
return (
<div>
<button onClick={() => setIsMounted(!isMounted)}>
{isMounted ? 'Unmount' : 'Mount'}
</button>
</div>
);
}
A terceira regra implícita é que os hooks devem ser importados do React (ou de uma biblioteca de hooks), e não implementados ad hoc.Isso é óbvio, mas vale a pena mencionar: a mágica está no despachante de hooks interno do React, que rastreia as chamadas de hooks entre as renderizações.
Gerenciando o estado local corretamente com useState
useState Permite associar estado a um componente de função e receber tanto o valor atual quanto uma função de atualização.Conceitualmente, é a contraparte funcional de this.state e this.setState em componentes de classe.
Um contraexemplo mínimo com useState se parece com isso:
import React, { useState } from 'react';
function Counter() {
const = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
export default Counter;
Quando Você ligar useState(initialValue)O React armazena esse estado e retorna um par.: o valor do estado atual e um método setter. Ao contrário das variáveis locais normais, o estado persiste entre renderizações, portanto o count O valor não é redefinido para 0 toda vez que a função do componente é executada.
Você pode usar qualquer valor serializável para o estado: números, strings, booleanos, arrays, objetos e até mesmo funções.. Você também pode ligar useState Várias vezes no mesmo componente para manter os valores relacionados separados, em vez de colocar tudo em um único objeto.
Quando o novo valor do estado depender do anterior, utilize sempre o formulário de atualização funcional.Isso evita erros quando várias atualizações de estado ocorrem em rápida sucessão:
setCount(prev => prev + 1);
Outro detalhe sutil, mas importante, é que chamar o setter substitui todo o valor do estado; não mescla objetos como em `single`. this.setState nas aulasSe o seu estado for um objeto ou uma matriz, você precisa espalhar o valor anterior manualmente:
const = useState({ name: 'Alex', age: 30 });
// ✅ Correct: copy and update
setUser(prev => ({ ...prev, age: prev.age + 1 }));
Para valores iniciais dispendiosos, você pode inicializar o estado de forma preguiçosa, passando uma função para useStateO React só o chamará na primeira renderização:
const = useState(() => calculateInitialValue());
Lidar com efeitos colaterais com useEffect
useEffect é a API do React para executar efeitos colaterais em componentes funcionais.Um "efeito colateral" é qualquer coisa que interaja com o mundo exterior: busca de dados, registro de logs, alterações diretas no DOM, assinaturas, temporizadores, APIs do navegador, etc.
Conceitualmente, useEffect substitui uma combinação de componentDidMount, componentDidUpdate e componentWillUnmount de componentes de classeEm vez de dividir um efeito em três métodos de ciclo de vida, você o declara uma vez e deixa o React lidar com quando ele é executado e quando é finalizado.
A assinatura básica é useEffect(setup, dependencies?). O setup A função é o corpo do seu efeito; opcionalmente, ela pode retornar uma função de limpeza. dependencies O array informa ao React quando o efeito precisa ser executado novamente.
useEffect(() => {
// side effect logic here
return () => {
// optional cleanup logic here
};
}, );
Por padrão, sem o segundo argumento, o efeito será executado após cada renderização. (primeira montagem e todas as atualizações subsequentes). Isso geralmente é demais para as requisições de rede ou para lógica complexa.
Um padrão muito comum é atualizar algo externo sempre que um elemento de estado muda.Por exemplo, atualizar o título da página dependendo da quantidade de cliques:
import React, { useState, useEffect } from 'react';
function Counter() {
const = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
}, ); // effect re-runs only when `count` changes
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increase</button>
</div>
);
}
A matriz de dependências é crucial para o desempenho e a correção.Ele controla quando o React deve executar o efeito novamente: se alguma dependência tiver sido alterada de acordo com Object.is Em comparação, o efeito é finalizado e executado novamente; se nada mudou, ele é ignorado.
Entendendo a matriz de dependências como um profissional
A matriz de dependências é onde se encontram as nuances mais complexas. useEffect os insetos vêm deO React compara cada elemento da matriz com seu valor anterior usando Object.isSe todos os valores forem iguais, o efeito é ignorado; se pelo menos um for diferente, o efeito é executado novamente.
Existem três configurações principais de dependência que você usará o tempo todo.:
- Sem segundo argumentoO efeito é executado após cada renderização.
- Matriz vazia
[]O efeito é executado apenas uma vez ao montar o sistema e é removido ao desmontá-lo. - Matriz com valores
O efeito é executado após a montagem e sempre que alguma dependência for alterada.
Quando as dependências são valores primitivos (números, strings, booleanos), isso é simples.Os problemas começam quando você coloca objetos, arrays ou funções dentro das dependências, porque a igualdade é baseada em referência. Dois objetos idênticos com referências diferentes são considerados "diferentes", o que causa novas execuções a cada renderização.
Considere um efeito que depende de um team objeto de adereços:
function Team({ team }) {
useEffect(() => {
console.log(team.id, team.active);
}, ); // ⚠️ might re-run every render if `team` reference changes
}
Mesmo que o conteúdo da equipe em si não mude, uma nova referência de objeto em cada renderização forçará a execução do efeito novamente.Para evitar isso, dependa dos campos primitivos que você realmente usa ou reconstrua o objeto dentro do próprio efeito.
Uma versão mais segura rastreia apenas o que o efeito realmente precisa.:
function Team({ team }) {
const { id, active } = team;
useEffect(() => {
console.log(id, active);
}, );
}
Se você realmente precisa do objeto inteiro dentro do efeito, pode recriá-lo lá em vez de usá-lo como uma dependência.Dessa forma, a lista de dependências ainda pode ser baseada em tipos primitivos:
function Team({ team }) {
const { id, active, name } = team;
useEffect(() => {
const localTeam = { id, active, name };
// use `localTeam` here
}, );
}
Como último recurso, você pode usar a memoização com useMemo or useCallback para objetos ou funções dispendiosasMas lembre-se de que a memoização em si tem um custo. Não a utilize indiscriminadamente "por precaução"; adicione-a somente quando uma dependência específica estiver realmente causando problemas de desempenho.
Limpar os efeitos corretamente
Alguns efeitos colaterais alocam recursos que precisam ser liberados.Assinaturas, sockets, intervalos, tempos limite, ouvintes de eventos, etc. Esquecer de limpá-los pode facilmente levar a vazamentos de memória ou trabalho duplicado.
In useEffectA limpeza é feita retornando uma função do efeito.O React chamará essa função antes de executar o efeito novamente com novas dependências e também uma última vez quando o componente for desmontado.
import { useEffect } from 'react';
function LogMessage({ message }) {
useEffect(() => {
const log = setInterval(() => {
console.log(message);
}, 1000);
return () => {
clearInterval(log);
};
}, );
return <div>logging to console "{message}"</div>;
}
Neste exemplo, todas as vezes message As alterações fazem com que o React primeiro limpe o intervalo antigo e, em seguida, configure um novo com a mensagem atualizada.Quando o componente desaparece da interface do usuário, a última limpeza limpa o intervalo permanentemente.
Essa combinação de “preparação + limpeza” é fundamental para o modelo mental de useEffectTente pensar em cada efeito como um processo independente que começa na função de configuração e termina completamente na função de limpeza. O React pode executar vários ciclos de configuração/limpeza durante o desenvolvimento (especialmente no Modo Estrito) para testar se a sua limpeza realmente desfaz tudo.
Um exemplo clássico é a inscrição em uma fonte externa, como uma API de bate-papo ou um evento do navegador (veja Lidando com o evento onKeyDown no React):
useEffect(() => {
function handleClick(event) {
console.log('Clicked', event.clientX, event.clientY);
}
document.addEventListener('click', handleClick);
return () => {
document.removeEventListener('click', handleClick);
};
}, []); // runs once on mount, cleans up on unmount
Utilizando useState e useEffect em conjunto para buscar dados.
Uma das combinações mais comuns no mundo real é usar useState e useEffect para obter dados de uma APIVocê mantém os dados (e talvez indicadores de carregamento/erro) no estado e executa a solicitação em um efeito que é acionado quando o componente é montado ou quando algum parâmetro é alterado.
Um padrão básico para buscar dados após a montagem é o seguinte::
import { useEffect, useState } from 'react';
function FetchItems() {
const = useState([]);
useEffect(() => {
let ignore = false;
async function fetchItems() {
try {
const response = await fetch('/items');
const fetchedItems = await response.json();
if (!ignore) {
setItems(fetchedItems);
}
} catch (error) {
console.error('Error fetching items:', error);
}
}
fetchItems();
return () => {
// avoid updating state if the component unmounted
ignore = true;
};
}, []);
return (
<div>
{items.map(item => (
<div key={item.id ?? item}>{item.name ?? item}</div>
))}
</div>
);
}
Aqui, o array de dependências vazio garante que a requisição seja executada exatamente uma vez.. O interno ignore A flag é uma maneira simples de evitar definir o estado de um componente desmontado caso a solicitação seja resolvida tardiamente.
Também é muito comum adicionar um indicador de carregamento e exibir um spinner ou um marcador enquanto os dados estão sendo transferidos.:
const Statistics = () => {
const = useState([]);
const = useState(true);
useEffect(() => {
const getStats = async () => {
try {
const statsData = await getData();
setStats(statsData);
} finally {
setLoading(false);
}
};
getStats();
}, []);
if (loading) {
return <div>Loading statistics...</div>;
}
return (
<ul>
{stats.map(stat => (
<li key={stat.id}>{stat.label}: {stat.value}</li>
))}
</ul>
);
};
Se a sua consulta depende de um parâmetro (como uma categoria, filtro ou parâmetro de rota), adicione esse parâmetro à matriz de dependências. Assim, o efeito se repete quando há uma mudança:
useEffect(() => {
async function fetchItems() {
const response = await fetch(`/items?category=${category}`);
const data = await response.json();
setItems(data);
}
fetchItems();
}, );
Pensar em "efeitos em cada renderização" versus "ciclos de vida"
Se você está acostumado com componentes de classe, pode ser tentador mapeá-los mentalmente. useEffect métodos de montagem/atualização/desmontagemMas isso geralmente leva a mais confusão. Um modelo mental mais simples é: "os efeitos são executados após a renderização e podem ser finalizados antes da próxima execução".
Nas aulas, muitas vezes era necessário duplicar a lógica entre componentDidMount e componentDidUpdate Porque você queria que o mesmo efeito fosse executado tanto na montagem quanto nas atualizações. Com os hooks, essa duplicação desaparece: um único efeito abrange ambos os casos, e o React se encarrega da limpeza entre as execuções.
Este design também elimina toda uma classe de bugs relacionados ao tratamento incorreto de atualizações.Por exemplo, em um componente de classe que se inscreve no status online de um amigo, é fácil esquecer de se inscrever novamente quando... props.friend alterações, causando assinaturas obsoletas ou falhas ao desmontar. Com useEffect que lista friend.id Como dependência, o React executará automaticamente a limpeza do recurso antigo e a configuração do novo.
Lembre-se de que, no Modo Estrito de desenvolvimento, o React executa deliberadamente o ciclo de configuração e limpeza duas vezes na montagem.Isso não acontece em produção, mas é um teste de estresse útil para confirmar se a sua limpeza realmente desfaz tudo e se o seu efeito pode ser executado com segurança várias vezes.
Otimização e resolução de problemas de comportamento do efeito de uso
Quando um efeito é executado com mais frequência do que o esperado, a primeira coisa a verificar é a matriz de dependências.Ou uma dependência muda a cada renderização (comum com objetos/funções embutidos) ou você se esqueceu de especificar o array.
Registrar os valores de dependência é uma maneira rápida de depurar.:
useEffect(() => {
console.log('Effect deps:', dep1, dep2);
}, );
Se você observar registros diferentes a cada vez, verifique qual dependência está sendo alterada.Frequentemente, você encontrará um objeto embutido ou uma função de seta sendo recriada a cada renderização. Mover a criação de objetos para dentro do efeito, ou elevar as funções para fora do componente, ou memorizá-las com useCallback Pode estabilizar dependências quando necessário.
Loops infinitos ocorrem quando um efeito depende de um valor e, ao mesmo tempo, atualiza esse mesmo valor incondicionalmente.. Por exemplo:
useEffect(() => {
setCount(count + 1); // ⚠️ will cause a loop if `count` is a dependency
}, );
Toda vez count mudanças, o efeito se repete, atualizações count Novamente, isso aciona outra renderização, e assim por diante.Para quebrar esse padrão, considere se a atualização de estado realmente pertence a um efeito, se deveria ser acionada por uma interação do usuário ou se você pode depender de um valor diferente.
Às vezes, você deseja ler o valor mais recente de algum estado ou propriedade dentro de um efeito sem que esse valor acione uma nova execução.Nesses cenários avançados, APIs mais recentes como "eventos de efeito" (via useEffectEvent (conforme a documentação do React) ou refs podem ajudar, mas, na maioria dos casos práticos, manter-se fiel às dependências é mais seguro e simples.
Juntando tudo, usando useState e useEffect A solução correta se resume a alguns hábitos fundamentais.Mantenha o estado pequeno e focado, prefira atualizações funcionais ao derivar um novo estado do antigo, estruture os efeitos em torno de pares de configuração/limpeza, seja honesto e explícito com os arrays de dependências e sempre respeite as regras dos hooks para que o React possa rastrear de forma confiável o que pertence a cada lugar. Ao seguir esses princípios, seus componentes permanecem previsíveis, seus efeitos colaterais se comportam corretamente e sua base de código React se torna muito mais fácil de evoluir à medida que seu aplicativo cresce.