O erroProfundidade máxima de atualização excedida. Isso pode acontecer quando um componente chama repetidamente setState dentro de componentWillUpdate ou componentDidUpdate (ou em useEffect) significa que seu componente está preso em um loop de renderização infinito. Aqui estão todas as causas e sua solução.
📋 Table of Contents
- Por que isso acontece
- Causa 1: Chamando setState diretamente em Render/Body
- Causa 2: Chamar uma função em vez de passá-la
- Causa 3: useEffect atualizando sua própria dependência
- Causa 4: Objeto/matriz/função em dependências useEffect
- Causa 5: Definindo o estado em um manipulador de eventos que é acionado na renderização
- Como depurar o loop
- As regras de ouro
- Perguntas Frequentes
- Conclusão
Por que isso acontece
O React é renderizado novamente quando o estado muda. Se uma atualização de estado acionar uma renderização que acione outra atualização de estado, você obterá um loop infinito. O React detecta isso e lança “Profundidade máxima de atualização excedida” para evitar que seu navegador congele.
Causa 1: Chamando setState diretamente em Render/Body
// 🐛 setState called during render → triggers re-render → loops forever
function Counter() {
const [count, setCount] = useState(0);
setCount(count + 1); // ❌ runs on every render → infinite loop
return <div>{count}</div>;
}
// ✅ Only update state in event handlers or effects
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
Causa 2: Chamar uma função em vez de passá-la
// 🐛 onClick={handleClick()} CALLS handleClick during render
<button onClick={handleClick()}>Click</button>
// If handleClick calls setState, it loops on every render
// ✅ Pass the function reference, don't call it
<button onClick={handleClick}>Click</button>
// ✅ Or wrap in an arrow function if you need to pass arguments
<button onClick={() => handleClick(id)}>Click</button>
Causa 3: useEffect atualizando sua própria dependência
// 🐛 Effect sets state that's in its dependency array
function Component() {
const [data, setData] = useState([]);
useEffect(() => {
setData([...data, newItem]); // ❌ changes data → effect re-runs → loop
}, [data]); // depends on data, which the effect changes
}
// ✅ Use a functional update and remove data from deps
function Component() {
const [data, setData] = useState([]);
useEffect(() => {
setData(prev => [...prev, newItem]); // functional update
}, []); // runs once — no dependency on data
}
Causa 4: Objeto/matriz/função em dependências useEffect
// 🐛 New object reference every render → effect always re-runs
function Component({ userId }) {
const options = { id: userId }; // NEW object each render
useEffect(() => {
fetchData(options);
setState(...);
}, [options]); // options is "new" every render → infinite loop
}
// ✅ Depend on the primitive value, or memoize the object
function Component({ userId }) {
useEffect(() => {
fetchData({ id: userId });
setState(...);
}, [userId]); // primitive — stable across renders
// OR memoize:
const options = useMemo(() => ({ id: userId }), [userId]);
}
Causa 5: Definindo o estado em um manipulador de eventos que é acionado na renderização
// 🐛 onChange fires and setState causes re-render, which re-triggers...
// often from passing changing props to controlled components incorrectly
// 🐛 Also common: updating parent state that re-renders and resets child
function Parent() {
const [value, setValue] = useState('');
return <Child onValueChange={setValue(newValue)} />; // ❌ called in render
}
// ✅ Pass the setter reference
function Parent() {
const [value, setValue] = useState('');
return <Child onValueChange={setValue} />; // pass reference
}
Como depurar o loop
// 1. Add a log to see how often the component renders
function Component() {
console.log('render'); // if this floods the console, you have a loop
// ...
}
// 2. Log inside the effect to find the culprit
useEffect(() => {
console.log('effect running');
// ...
}, [dep1, dep2]);
// 3. Log each dependency to see which one keeps changing
useEffect(() => {
console.log('dep1:', dep1, 'dep2:', dep2);
}, [dep1, dep2]);
// 4. The React DevTools Profiler highlights components re-rendering rapidly
As regras de ouro
- Nunca chame setState diretamente no corpo do componente — apenas em manipuladores de eventos, efeitos ou retornos de chamada
- Passar referências de função para manipuladores de eventos, não os chame (
onClick={fn}notonClick={fn()}) - Use atualizações funcionais (
setState(prev => ...)) quando o novo estado depender do estado antigo - Dependem de primitivas em useEffect ou memorize objetos/funções com useMemo/useCallback
- Não coloque o estado nas dependências de um useEffect se o efeito atualizar esse estado
Perguntas Frequentes
P: Qual é a diferença entre isso e “Muitas re-renderizações”?
R: Eles estão intimamente relacionados — ambos são loops de renderização infinitos. “Muitas re-renderizações” geralmente significa que setState é chamado diretamente durante a renderização; “Profundidade máxima de atualização excedida” geralmente indica que um método useEffect ou ciclo de vida está causando o loop. As correções se sobrepõem.
P: Por que passar setState diretamente causa um loop?
R: Se você escreveronClick={setState(x)}, que CHAMA setState durante a renderização (não ao clicar). setState aciona uma nova renderização, que a chama novamente – loop infinito. PassaronClick={() => setState(x)} em vez de.
P: Como atualizo o estado com base no estado anterior com segurança?
R: Use uma atualização funcional:setCount(prev => prev + 1). Isso não requercount na sua matriz de dependência, evitando uma causa de loop comum.
P: Minha dependência de objeto continua causando loops. O que eu faço?
R: Objetos e arrays obtêm novas referências a cada renderização. Depende de valores primitivos dentro deles ([obj.id]not[obj]) ou memorize o objeto comuseMemo(() => obj, [deps]).
P: O ESLint pode capturar isso?
R: Oeslint-plugin-react-hooks A regra de dependências exaustivas sinaliza muitos problemas de dependência. Ele não captura todos os loops, mas evita os erros de dependência useEffect mais comuns. Instale e habilite-o.
Conclusão
“Profundidade máxima de atualização excedida” é sempre um loop de renderização infinito. As causas mais comuns: chamar setState diretamente no corpo de renderização, chamar (em vez de passar) uma função em um manipulador de eventos, um useEffect que atualiza sua própria dependência ou dependências instáveis de objeto/função. As correções:atualiza apenas o estado em manipuladores e efeitos, passa referências de função, usa atualizações funcionais e depende de primitivos ou valores memorizados. Adicione umconsole.log('render') para confirmar o loop e, em seguida, registre cada dependência para descobrir qual delas continua mudando — o culpado geralmente fica óbvio quando você o vê.
🔗 Share this article
✍️ Leave a Comment