React

Riprovare dopo un errore di Suspense

Suspense non ha un pulsante di retry: si riprova rimontando il confine con una key nuova e invalidando la cache che ha lanciato la promise.

In sintesi

  • Suspense gestisce la promise pendente, l’error boundary gestisce il rifiuto: servono entrambi.
  • Cambiare la key del confine smonta il sottoalbero e azzera stato di errore e promise pendente.
  • Se la cache conserva la promise rifiutata, il rimontaggio legge la stessa rejection: va svuotata prima.

Suspense non offre un retry: per riprovare si rimonta il confine con una key nuova e si invalida la cache che ha lanciato la promise. Senza i due passaggi il confine rilegge lo stesso rifiuto e resta sull’errore.

Chi cattura cosa

Suspense intercetta una promise lanciata durante il render e mostra il fallback finché non si risolve. Un errore, compresa una promise rifiutata letta da use(), non passa da Suspense: va all’error boundary più vicino. Per questo un retry completo ha bisogno di entrambi i confini.

Lo schema con la key

Il contatore di tentativi vive sopra l’error boundary e diventa la key del confine stesso: cambiare la key equivale a costruire un confine nuovo.

function Panel({ id }) {
  const [attempt, setAttempt] = useState(0);

  return (
    
          Load failed{" "}
           setAttempt(attempt + 1)}>Try again
        
      }
    >
      Loading}>
        
      
    
  );
}

Perché la key basta per lo stato

React tratta una key diversa come un componente diverso: smonta il sottoalbero, scarta lo stato dell’error boundary e riparte con il render iniziale. Anche la promise pendente sparisce con il sottoalbero, quindi il nuovo tentativo parte da zero.

La cache resta

Se il livello dati conserva la promise rifiutata, il rimontaggio la ritrova e rilancia lo stesso errore. Prima di incrementare il tentativo va tolta la voce, altrimenti il pulsante sembra non fare niente.

async function retry() {
  await cache.delete(`details:${id}`);
  setAttempt((n) => n + 1);
}

L’ordine conta: la cancellazione attende, poi il contatore cambia. Se si inverte, il nuovo confine può leggere la voce vecchia prima che sparisca.

Il comando

Il pacchetto react-error-boundary offre resetKeys e onReset, così il contatore diventa la chiave di reset senza scrivere un boundary a mano.

npm install react-error-boundary
npm ls react-error-boundary

Tre record che mostrano uno stato di caricamento prima dei dati: leads-data-table riempie una tabella da una fonte remota, project-data-table filtra righe arrivate dopo, area-chart disegna solo quando la serie è pronta. Sono record fra i 111.985 componenti del catalogo.

Fonte e licenza

React è distribuito con licenza MIT e react-error-boundary con licenza MIT. La documentazione React su Suspense descrive il fallback e quella sugli error boundary il recupero: le due pagine vanno lette insieme, perché il retry vive nell’intersezione.

La limitazione

Il retry per rimontaggio perde lo stato locale del sottoalbero, la posizione di scroll e il focus. Un retry automatico su un errore 500 moltiplica le richieste, quindi il numero di tentativi va limitato e il pulsante manuale resta la scelta predefinita.

Dove continuare

Le basi sono in Suspense; il confine che cattura gli errori è in error boundary pattern; il confronto con lo streaming di Next.js è in streaming e Suspense in Next.js.

Domande frequenti

Perché Suspense non ha un retry integrato?

Perché non sa chi ha lanciato la promise né come invalidarla. Il retry appartiene al livello dati, che conosce la chiave della cache, e al confine, che può rimontare.

Serve una key nuova o basta un setState?

Serve una key nuova sul confine, oppure resetKeys se usi react-error-boundary. Un setState nello stesso confine non cancella lo stato di errore.

Il retry funziona senza error boundary?

No: una promise rifiutata lancia un errore che senza boundary risale fino alla radice e smonta l’app. Il boundary è la condizione per mostrare un pulsante invece di una pagina bianca.

Posso fare retry automatico?

Sì, con un effetto che incrementa il tentativo dopo un ritardo, ma va limitato: senza tetto un errore 500 produce una raffica di richieste e consuma quota.

Cosa succede allo stato del componente rimontato?

Si perde. Il rimontaggio è un componente nuovo, quindi stato locale, scroll e focus tornano ai valori iniziali: se vanno conservati, teneteli sopra il confine.