Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.O React está lhe dizendo que a renderização causou uma atualização de estado, que causou uma renderização, que causou uma atualização. A causa é quase sempre um dos quatro padrões, e cada um tem uma solução clara.
📋 Table of Contents
- O que realmente está acontecendo
- Causa 1: Chamar o manipulador em vez de passá-lo
- Causa 2: useEffect sem uma matriz de dependência
- Causa 3: uma dependência que altera cada renderização
- Causa 4: Configurando o estado para derivar um valor
- A variante de renderização condicional
- Encontrando o culpado
- Referência rápida
- Perguntas Frequentes
- Conclusão
O que realmente está acontecendo
A renderização deve ser um cálculo puro da IU a partir de adereços e estado. Se também definir o estado, você cria um ciclo. O React detecta a fuga e a interrompe, em vez de deixar a guia congelar.
function Counter() {
const [count, setCount] = useState(0);
setCount(count + 1); // runs during render -> triggers render -> runs again
return <div>{count}</div>;
}
A mensagem de erro é excepcionalmente precisa aqui: algo em seu caminho de renderização está atualizando o estado toda vez que é executado.
Causa 1: Chamar o manipulador em vez de passá-lo
De longe a versão mais comum e que surpreende a todos pelo menos uma vez.
function App() {
const [count, setCount] = useState(0);
// ❌ setCount(count + 1) is CALLED during render.
// Its return value (undefined) becomes the onClick handler.
return <button onClick={setCount(count + 1)}>Increment</button>;
}
onClick precisa de uma função para chamar mais tarde. EscritasetCount(...) executa-o imediatamente. Envolva-o em uma função de seta para que uma função seja passada em vez de um resultado.
// ✅ A function is passed; it runs on click.
<button onClick={() => setCount(count + 1)}>Increment</button>
// ✅ Or reference an existing function without calling it.
<button onClick={handleClick}>Increment</button> // no parentheses
// ❌ This calls handleClick during render.
<button onClick={handleClick()}>Increment</button>
A regra:parênteses após o nome do manipulador em JSX significa que ele é executado durante a renderização. Se você precisar passar argumentos, envolva-os.
<button onClick={() => handleDelete(item.id)}>Delete</button>
Causa 2: useEffect sem uma matriz de dependência
Um efeito sem segundo argumento é executado após cada renderização. Se definir o estado, essa mudança de estado acionará outra renderização, que executará o efeito novamente.
function Profile({ userId }) {
const [user, setUser] = useState(null);
// ❌ No dependency array — runs after every render, forever.
useEffect(() => {
fetch(`/api/users/${userId}`).then(r => r.json()).then(setUser);
});
}
// ✅ Re-runs only when userId changes.
useEffect(() => {
fetch(`/api/users/${userId}`).then(r => r.json()).then(setUser);
}, [userId]);
Omitir o array é diferente de passar um array vazio. Nenhuma matriz significa “cada renderização”; [] significa “uma vez montado”.
Causa 3: uma dependência que altera cada renderização
Objetos, matrizes e funções são comparados por referência. Uma nova a cada renderização significa que a dependência sempre parece alterada.
function Search({ query }) {
const [results, setResults] = useState([]);
// New object identity on every render.
const options = { query, limit: 20 };
useEffect(() => {
search(options).then(setResults);
}, [options]); // ❌ always "changed" -> loop
}
Dependem de primitivos, que são comparados por valor.
useEffect(() => {
search({ query, limit: 20 }).then(setResults);
}, [query]); // ✅ string compares by value
Quando o objeto deve existir fora do efeito, memorize-o.
const options = useMemo(() => ({ query, limit: 20 }), [query]);
useEffect(() => {
search(options).then(setResults);
}, [options]); // ✅ identity is stable until query changes
Causa 4: Configurando o estado para derivar um valor
Usar o estado mais um efeito para calcular algo a partir de valores existentes cria um ciclo desnecessário e uma renderização extra.
function Cart({ items }) {
const [total, setTotal] = useState(0);
// ❌ Unnecessary, and loops if the dependency is wrong.
useEffect(() => {
setTotal(items.reduce((sum, i) => sum + i.price, 0));
});
return <p>Total: {total}</p>;
}
Qualquer coisa computável a partir de adereços ou estado deve ser computada durante a renderização.
function Cart({ items }) {
// ✅ No state, no effect, no possible loop.
const total = items.reduce((sum, i) => sum + i.price, 0);
return <p>Total: {total}</p>;
}
Se o cálculo for realmente caro, envolva-o emuseMemo – mas ainda não o coloque no estado.
const total = useMemo(
() => items.reduce((sum, i) => sum + i.price, 0),
[items]
);
A variante de renderização condicional
Definir o estado dentro de uma condicional durante a renderização é o mesmo bug do disfarce.
function Form({ initialValue }) {
const [value, setValue] = useState('');
// ❌ Still a state update during render.
if (initialValue && !value) {
setValue(initialValue);
}
}
Em vez disso, inicialize o estado corretamente. Se o valor inicial for caro para calcular, passe uma função para que ela seja executada apenas uma vez.
const [value, setValue] = useState(initialValue ?? '');
// Lazy initialiser — the function runs on the first render only.
const [rows, setRows] = useState(() => parseLargeCsv(raw));
Se o valor precisar ser redefinido quando uma propriedade for alterada, a solução idiomática serákey no componente, que o remonta com estado fresco.
<Form key={userId} initialValue={user.name} />
Encontrando o culpado
Quando a causa não for óbvia, registre a origem das renderizações.
function MyComponent(props) {
console.count('MyComponent render');
console.log('props:', props);
// ...
}
React DevTools Profiler com “Registrar por que cada componente renderizado” habilitado informa qual suporte ou estado foi alterado em cada passagem. Para loops de efeitos, faça login dentro do efeito e dentro do setter para ver o ciclo diretamente.
useEffect(() => {
console.log('effect ran, deps:', { userId, options });
}, [userId, options]);
Referência rápida
| Sintoma | Causa provável | Correção |
|---|---|---|
| Erros instantaneamente na montagem | Manipulador chamado durante a renderização | Enrole em uma função de seta |
| Loops após carregamento de dados | Efeito faltando uma matriz de dependência | Adicionar[] ou as dependências corretas |
| Loops apesar das dependências corretas | Dependência de objeto ou função | Use primitivos,useMemo, ouuseCallback |
| Renderização extra a cada alteração | Valor derivado armazenado no estado | Calcular durante a renderização |
| Loops condicionais | setState dentro do branch de renderização | Inicialize o estado ou usekey |
Perguntas Frequentes
P: Por que isso só acontece às vezes?
R: Loops que dependem de dados precisam que os dados cheguem primeiro. O bug está presente desde o início, mas só é acionado quando uma busca é resolvida e define o estado.
P: É aceitável definir o estado durante a renderização?
R: O React permite um caso restrito – ajustar o estado durante a renderização quando um suporte muda, atualizando apenas o mesmo componente e protegido por uma comparação. Raramente é a melhor resposta; prefira computar durante a renderização ou usarkey.
P: Isso acontece nos componentes da classe?
R: Sim. ChamandosetState in render ou incondicionalmente emcomponentDidUpdate produz o loop idêntico.
P: Meu efeito tem um array vazio, mas ainda faz loop. Por que?
R: Então o loop está em outro lugar – na maioria das vezes um manipulador invocado durante a renderização ou um pai remontando o filho porque ékey muda a cada renderização.
P: O React Compiler corrigirá isso para mim?
R: Ele remove a categoria de memorização do problema, manipulando a identidade automaticamente. Isso não corrige a chamada de um manipulador durante a renderização, o que é um erro direto no JSX.
Conclusão
“Muitas novas renderizações” sempre significa que o estado está sendo atualizado como parte da renderização. Verifique as quatro causas em ordem:um manipulador chamado em vez de passado em JSX, umuseEffectsem matriz de dependência, uma dependência de objeto ou função que altera a identidade a cada renderização e valores derivados armazenados no estado em vez de computados. A regra subjacente é simples: a renderização deve ser um cálculo puro e cada atualização de estado deve se originar de um manipulador de eventos ou de um efeito, nunca da renderização em si.
🔗 Share this article
✍️ Leave a Comment