← React: componentes, estado e decisões de interface
07 / 10 · 60 MIN

Dados assíncronos, Suspense e limites de erro

Distinguir espera, falha e resultado obsoleto. Integrar leitura de Promises com Suspense e Error Boundaries e definir a aceitação de respostas que chegam fora de ordem.

Separar espera, falha e dados obsoletos

Uma interface assíncrona não tem apenas dados ou ausência de dados. Pode estar à espera, mostrar um resultado anterior enquanto atualiza, receber uma falha ou receber uma resposta que já não corresponde à seleção. Identifica primeiro o recurso e a tentativa a que cada resultado pertence.

Uma resposta que chegou agora pode ser antiga do ponto de vista da intenção do utilizador. O exemplo recebe reportPromise e attemptId do chamador para tornar essa relação explícita. Não deduz sucesso a partir do desaparecimento de um indicador de carregamento.

O contrato deve dizer que resultado foi aceite, qual falhou e que ação inicia uma nova tentativa.

O que Suspense observa

Suspense fornece um fallback quando conteúdo integrado com o seu mecanismo ainda não está pronto. Entre os casos ensinados aqui estão código carregado através de lazy e leitura de uma Promise com use. Iniciar fetch num Effect ou num handler não ativa automaticamente a boundary.

Se esse fluxo usa estado próprio, precisa de representar carregamento e falha nesse contrato, ou usar uma integração apropriada do framework. O nome de uma variável loading não altera este comportamento. Esta aula limita-se à leitura de dados e código; não depende de funcionalidades experimentais nem dos mecanismos específicos de animação e carregamento de recursos visuais de React 19.3.

Ler uma Promise com identidade estável

use(reportPromise) devolve o valor resolvido, suspende enquanto a Promise está pendente e encaminha a rejeição para o limite de erro apropriado. A Promise precisa de ser conservada em cache para reutilizar a mesma instância entre renders. Criar loadReport(id) em cada render, quando essa função cria sempre uma Promise nova, não cumpre esse contrato.

O chamador do exemplo é responsável pela cache e por novas tentativas. use deve ser chamado no contexto de um componente ou Hook e não deve ser envolvido num try/catch local para substituir o protocolo de suspensão. Quando o valor vem de um Server Component para um Client Component, também existe o requisito de serialização desse valor.

Definir o limite de erro

Uma Error Boundary permite substituir uma subárvore que falhou durante a renderização por uma apresentação de erro. No exemplo, getDerivedStateFromError atualiza estado de forma pura e render escolhe o fallback. componentDidCatch pode acrescentar observação da falha quando necessário.

A boundary não protege erros lançados por si própria e não captura, pelo mesmo mecanismo normal, erros em handlers comuns, callbacks de timers ou renderização do servidor. A documentação distingue a exceção de erros dentro de Actions de transição.

Decide a granularidade pelo produto: um relatório pode falhar sem apagar a navegação que permite escolher outro. Uma boundary em cada elemento pequeno não é automaticamente uma melhor arquitetura.

Resultados de Effects fora de ordem

Noutro desenho, um Effect inicia um pedido quando muda o id. A inicia primeiro, B é selecionado depois, mas B responde antes de A. Aplicar sempre a última resposta a chegar pode mostrar o corpo de A sob o título de B.

O padrão documentado usa um guard local invalidado no cleanup para não aplicar o resultado do Effect anterior. Essa decisão protege a aceitação local; não prova que o transporte ou o trabalho no servidor tenham sido cancelados. O mesmo raciocínio deve abranger sucesso e falha para não mostrar um erro antigo sobre uma seleção nova.

Também é preciso definir o estado apresentado enquanto o pedido atual ainda não terminou.

Recuperação inclui o recurso

Mostrar um fallback é contenção da falha, não uma nova tentativa. Se a cache continua a fornecer a mesma Promise rejeitada, mudar apenas a key da boundary não transforma essa Promise em sucesso. A recuperação deve coordenar a política de cache, a nova tentativa e o reset visual necessário.

No exemplo, attemptId só tem sentido se corresponder à tentativa de recurso fornecida. Evita um botão que apenas repete o erro sem explicar o que mudou. Também distingue recuperar uma leitura de repetir uma escrita: esta aula não estabelece que uma operação externa possa ser repetida sem efeitos adicionais. O contrato deve identificar a operação antes de escolher retry.

Revisão de uma vista assíncrona

Lê a vista em quatro momentos: antes dos dados, com resultado válido, com rejeição e após uma mudança de seleção. Verifica qual boundary trata espera, qual trata erro e que partes da interface continuam disponíveis. Confirma depois a identidade do recurso e a regra de aceitação de respostas.

Uma implementação pode usar mecanismos de dados de um framework sem que React, por si, defina toda a cache ou a política de rede. As avaliações apresentam as premissas necessárias e pedem escolher o próximo comportamento correto. O excerto é uma leitura de contratos de componentes; não exige criar infraestrutura ou executar pedidos para responder.

import {Component, Suspense, use} from 'react';

class ReportErrorBoundary extends Component {
  state = {failed: false};
  static getDerivedStateFromError() { return {failed: true}; }
  render() {
    if (this.state.failed) return <p>Report unavailable. Choose a new attempt.</p>;
    return this.props.children;
  }
}

function ReportBody({reportPromise}) {
  const report = use(reportPromise);
  return <article><h2>{report.title}</h2><p>{report.summary}</p></article>;
}

// The caller owns caching/retry. Promise identity stays stable per attempt.
// A fulfilled report has string title and summary fields.
export default function ReportPanel({reportPromise, attemptId}) {
  return <ReportErrorBoundary key={attemptId}>
    <Suspense fallback={<p>Loading report...</p>}>
      <ReportBody reportPromise={reportPromise} />
    </Suspense>
  </ReportErrorBoundary>;
}
NA PRÁTICA

O chamador fornece uma Promise estável por tentativa. Enquanto está pendente, use integra a leitura com Suspense; se rejeitar, a Error Boundary apresenta a falha. Uma nova tentativa deve fornecer o recurso correspondente e alterar attemptId quando se pretende reinicializar a boundary. O excerto não implementa transporte, cache ou botão de repetição.

Armadilhas comuns

Esperar que Suspense observe qualquer fetch; criar uma Promise por render; capturar use com try/catch; confundir ignorar uma resposta com cancelar o servidor; redefinir apenas a boundary mantendo uma Promise rejeitada.

Tópicos relacionados: Effects, refs e sincronização externa · Desempenho, transições e decisões de renderização · Entrega, hidratação e limites do servidor

Leva esta ideia contigo

A leitura assíncrona precisa de identidade, estado de espera, limite de erro e política de recuperação. Suspense trata leituras integradas; resultados obsoletos e novas tentativas precisam de contratos explícitos.

Criar conta

Referência: Out-of-order Effect results · React 19.3.0; JSX