🌐 Detecting your location…

Como corrigir o erro ‘Muitas re-renderizações’ no React: solução completa

⏱️6 min read  ·  1,286 words

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.

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.

MD Rafikul Islam

Written by

MD Rafikul Islam is a software developer and the editor of TechPulse. He writes about developer tooling, hardware, and the practical decisions that come up in day-to-day engineering work — which laptop to buy, which framework to commit to, why a build broke at 2am. He tests the tools he writes about and says plainly when something is not worth the money. Corrections and corrections requests are welcome at rony.yf25@gmail.com.

✍️ Leave a Comment

Your email address will not be published. Required fields are marked *

🌐 Read in:🇬🇧 English🇩🇪 Deutsch🇧🇷 Português🇸🇦 العربية🇮🇳 हिन्दी🇧🇩 বাংলা