Diego Betto's Blog
Foto di Andrew Wulf su Unsplash

Diego Betto · 14 settembre 2026 · 5 min di lettura

React 19: "Too many re-renders", l'errore del loop infinito spiegato

Perché React lancia 'Too many re-renders. React limits the number of renders...' e le due cause più comuni: setState nel corpo del render e dipendenze instabili negli effect.

Condividi:XLinkedInFacebookWhatsApp

Cambi un paio di righe, salvi, e il browser si blocca per un secondo prima di buttarti in console questo:

⛔ Errore

Too many re-renders. React limits the number of renders to prevent an infinite loop.

Il messaggio ti dice che React ha intercettato il loop prima che il tab andasse davvero in crash — è la buona notizia. La cattiva è che non ti dice quale riga l’ha causato. Quasi sempre il colpevole è uno di due pattern, entrambi facilissimi da scrivere per errore.

Causa #1: chiamare setState direttamente nel corpo del render

È il caso classico, e a prima vista sembra ragionevole:

function Counter() {
  const [count, setCount] = useState(0);

  // Gira ad ogni render — incluso quello che ha appena innescato
  setCount(count + 1);

  return <p>{count}</p>;
}

Ogni chiamata a setCount pianifica un re-render. Quel re-render riesegue il corpo del componente, che richiama setCount, che pianifica un altro re-render — all’infinito. La funzione di render di React deve essere pura: può leggere lo stato, ma non può scriverlo incondizionatamente come parte del render.

La correzione è quasi sempre spostare l’aggiornamento dello stato in un event handler o in un effect che gira solo quando cambia qualcosa di specifico:

function Counter() {
  const [count, setCount] = useState(0);

  return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

⚠ Una variante più subdola

Succede anche indirettamente, quando chiami setState condizionalmente durante il render per “sincronizzare” le props nello stato: if (value !== prevValue) setState(value). Sembra innocuo grazie all’if, ma se la condizione risulta vera ad ogni render — per esempio perché value è un nuovo oggetto letterale ogni volta — sei di nuovo in un loop infinito. Vedi la causa #2 qui sotto per capire esattamente perché succede.

Causa #2: un oggetto o array ricreato ad ogni render come dipendenza

Questa è più subdola perché la chiamata a setState è tranquillamente dentro un useEffect, il che sembra corretto:

function SearchResults({ query }) {
  const [results, setResults] = useState([]);

  // Nuovo oggetto ad ogni render!
  const options = { limit: 10, query };

  useEffect(() => {
    fetchResults(options).then(setResults);
  }, [options]); // options non è mai === all'options precedente

  return (
    <ul>
      {results.map((r) => (
        <li key={r.id}>{r.text}</li>
      ))}
    </ul>
  );
}

options è un oggetto letterale nuovo di zecca ad ogni render, quindi non è mai uguale al precedente per riferimento. React riesegue l’effect ogni volta, l’effect chiama setResults, che innesca un re-render, che crea un nuovo oggetto options — un loop infinito, solo un passaggio più in là rispetto alla causa #1.

Due modi per risolverlo:

// Opzione A: dipendi dai valori primitivi, non dall'oggetto
useEffect(() => {
  fetchResults({ limit: 10, query }).then(setResults);
}, [query]);

// Opzione B: memoizza l'oggetto stesso
const options = useMemo(() => ({ limit: 10, query }), [query]);
useEffect(() => {
  fetchResults(options).then(setResults);
}, [options]);

L’opzione A è di solito più semplice ed è quella che sceglierei per prima — dipendi dai valori grezzi che cambiano davvero, e costruisci l’oggetto dentro l’effect dove la sua identità non conta.

💡 Consiglio

Con il React Compiler, buona parte di questa classe di bug diventa più facile da evitare perché il Compiler memoizza automaticamente oggetti e funzioni al posto tuo — ma non riscrive una chiamata setState che sta direttamente nel corpo del render, quindi la causa #1 resta interamente a tuo carico. Approfondisco dove la memoizzazione del Compiler aiuta davvero nell’articolo dedicato al React Compiler.

Come trovare davvero la riga incriminata

Il messaggio d’errore da solo non punta al tuo codice, ma due strumenti sì:

  • L’overlay di React in sviluppo di solito include uno stack di componenti subito sotto l’errore — leggilo dall’alto verso il basso, il primo componente scritto da te (non un wrapper di libreria) è il punto di partenza migliore.
  • Commenta gli aggiornamenti di stato uno alla volta. Sembra rozzo, ma per un loop con più coppie useEffect/useState nello stesso componente, disabilitare temporaneamente ogni chiamata setX finché il loop non si ferma è spesso più veloce che leggere stack trace generati.

FAQ

❓ 'Maximum update depth exceeded' è lo stesso errore?

Sì — è il messaggio Error sottostante che React lancia; “Too many re-renders” è il testo più amichevole mostrato dall’overlay di errore di React in sviluppo. Entrambi indicano lo stesso problema.

❓ useMemo o React.memo possono causarlo?

Non da soli — influenzano solo se un componente salta un render, non se ne pianifica uno. Il loop nasce sempre da una chiamata setState che scatta (di fatto) ad ogni render; useMemo/React.memo possono nascondere o rivelare il pattern, ma non sono la causa di fondo.

❓ Succede solo con gli hook?

È più comune con useState/useEffect, ma lo stesso tipo di bug esiste anche nei componenti a classi — chiamare this.setState dentro render(), o dentro componentDidUpdate senza una condizione adeguata, produce lo stesso identico loop infinito.

Conclusione

“Too many re-renders” riconduce sempre a un aggiornamento di stato che scatta incondizionatamente, che sia una chiamata setState piazzata direttamente nel corpo del render, o una nascosta dentro un effect la cui dipendenza viene ricreata ad ogni render. Una volta che sai cercare esattamente questi due pattern, la correzione è di solito una modifica di una riga — la parte difficile è capire quale delle tue coppie useState/useEffect è quella incriminata.

Riferimenti

Condividi:XLinkedInFacebookWhatsApp
Diego Betto

Scritto da

Diego Betto

Co-Fondatore & CTO presso PAPION. Senior full-stack engineer specializzato in React, TypeScript, Node.js e sicurezza applicativa.