Busque em todas as páginas da documentação
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
O SWR captura erros lançados pelo seu fetcher e os expõe através do valor de retorno error. Configure retentativas automáticas, callbacks de erro e integre com React Error Boundaries para um tratamento de erro robusto.
"use client";
import useSWR from "swr";
const fetcher = async (url: string) => {
const res = await fetch(url);
if (!res.ok) {
const error = new Error("Ocorreu um erro ao buscar os dados.");
(error as any).info = await res.json();
(error as any).status = res.status;
throw error;
}
return res.json();
};
function UserProfile({ id }: { id: string }) {
const { data, error } = useSWR(`/api/users/${id}`, fetcher, {
onError: (err) => console.error("Erro SWR:", err),
shouldRetryOnError: true,
errorRetryCount: 3,
errorRetryInterval: 5000,
});
if (error) return <div>Erro: {error.message}</div>;
return <div>{data?.name}</div>;
}"use client";
import useSWR from "swr";
import { Component, ReactNode } from "react";
// Classe de erro customizada com contexto adicional
class ApiError extends Error {
status: number;
info: Record<string, unknown>;
constructor(message: string, status: number, info: Record<string, unknown>) {
super(message);
this.status = status;
this.info = info;
}
}
const fetcher = async (url: string) => {
const res = await fetch(url);
if (!res.ok) {
const info = await res.json().catch(() => ({}));
throw new ApiError(
`Erro na API: ${res.statusText}`,
res.status,
info
);
}
return res.json();
};
// Componente Error Boundary
class ErrorBoundary extends Component<
{ children: ReactNode; fallback: ReactNode },
{ hasError: boolean }
> {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
render() {
if (this.state.hasError) return this.props.fallback;
return this.props.children;
}
}
function OrderDetails({ orderId }: { orderId: string }) {
const { data, error, isLoading } = useSWR(`/api/orders/${orderId}`, fetcher, {
shouldRetryOnError: (err: ApiError) => err.status !== 404,
errorRetryCount: 3,
errorRetryInterval: 2000,
onError: (err: ApiError) => {
if (err.status === 401) {
window.location.href = "/login";
}
},
});
if (isLoading) return <div>Carregando pedido...</div>;
if (error) {
if (error instanceof ApiError && error.status === 404) {
return <div>Pedido não encontrado</div>;
}
return <div>Algo deu errado: {error.message}</div>;
}
return (
<div>
<h2>Pedido #{data.id}</h2>
<p>Status: {data.status}</p>
<p>Total: ${data.total}</p>
</div>
);
}
export default function OrderPage({ orderId }: { orderId: string }) {
return (
<ErrorBoundary fallback={<div>Algo deu errado</div>}>
<OrderDetails orderId={orderId} />
</ErrorBoundary>
);
}error.errorRetryInterval.shouldRetryOnError pode ser true, false, ou uma função (err) => boolean para lógica de retentativa condicional.errorRetryCount limita o número total de tentativas de retentativa (padrão: ilimitado em conexões lentas).onError(err, key, config) é acionado em cada erro, incluindo retentativas.data mesmo quando uma revalidação falha. Isso significa que data e error podem ser definidos simultaneamente.onErrorRetry oferece controle total sobre o comportamento de retentativa, incluindo tempo e lógica de abortar.Retentativa customizada com backoff:
const { data } = useSWR("/api/data", fetcher, {
onErrorRetry: (error, key, config, revalidate, { retryCount }) => {
// Nunca retentar em 404
if (error.status === 404) return;
// Parar após 5 retentativas
if (retryCount >= 5) return;
// Backoff exponencial
setTimeout(() => revalidate({ retryCount }), Math.min(1000 * 2 ** retryCount, 30000));
},
});Manipulador de erro global:
<SWRConfig
value={{
onError: (error, key) => {
if (error.status !== 403 && error.status !== 404) {
reportToSentry(error, { key });
}
},
}}
>
{children}
</SWRConfig>Estado de erro com dados obsoletos:
function Dashboard() {
const { data, error, isValidating } = useSWR("/api/stats", fetcher);
return (
<div>
{error && (
<div className="bg-yellow-100 p-2">
Falha ao atualizar. Exibindo os últimos dados conhecidos.
{isValidating && " Tentando novamente..."}
</div>
)}
{data && <StatsDisplay stats={data} />}
</div>
);
}useSWR<Data, Error>(key, fetcher).const { data, error } = useSWR<User, ApiError>("/api/me", fetcher);
if (error) {
// error é tipado como ApiError
console.log(error.status); // number
console.log(error.info); // Record<string, unknown>
}error. Sempre valide res.ok no seu fetcher.data e error podem ambos ser truthy ao mesmo tempo. Isso acontece quando uma revalidação falha, mas dados em cache existem. Não assuma que são mutuamente exclusivos.errorRetryCount.onError é acionado em cada evento de erro, incluindo retentativas, o que pode inundar serviços de relatórios de erro. Use debounce ou deduplique em seu manipulador.| Abordagem | Prós | Contras |
|---|---|---|
| Retorno de erro do SWR | Declarativo, por componente | Deve ser tratado em cada componente |
| Error Boundary | Captura erros em tempo de renderização | Não captura erros assíncronos do SWR nativamente |
Callback onError global | Rastreamento centralizado de erros | Não pode afetar a renderização de componentes individuais |
| Notificações Toast | Feedback não bloqueante para o usuário | O usuário pode não ver a notificação |
O SWR retenta com backoff exponencial: 1s, 2s, 4s, 8s, etc., limitado por errorRetryInterval. A retentativa está habilitada por padrão sem um número máximo, a menos que você defina errorRetryCount.
const { data } = useSWR("/api/data", fetcher, {
shouldRetryOnError: (err) => err.status !== 404,
});Ou use onErrorRetry para controle total sobre a lógica de retentativa por tipo de erro.
Sim. Quando uma revalidação falha, mas dados em cache existem, tanto data quanto error são truthy. Não assuma que são mutuamente exclusivos. Exiba dados obsoletos com um banner de erro para a melhor experiência do usuário.
Error Boundaries capturam erros durante a renderização, não erros assíncronos. Erros do SWR são assíncronos e não se propagarão para Error Boundaries, a menos que você os relance durante a renderização ou use o modo suspense: true.
class ApiError extends Error {
status: number;
info: Record<string, unknown>;
constructor(message: string, status: number, info: Record<string, unknown>) {
super(message);
this.status = status;
this.info = info;
}
}Lance-a no seu fetcher quando res.ok for falso.
<SWRConfig
value={{
onError: (error, key) => {
if (error.status !== 403 && error.status !== 404) {
reportToSentry(error, { key });
}
},
}}
>
{children}
</SWRConfig>onError é acionado em cada evento de erro, incluindo cada tentativa de retentativa. Isso pode inundar serviços de relatórios de erro. Use debounce ou deduplique relatórios de erro em seu manipulador, ou limite as retentativas com errorRetryCount.
O SWR nunca definirá error. O corpo da resposta é tratado como dados bem-sucedidos. Sempre verifique res.ok em seu fetcher e lance um erro para códigos de status não 2xx.
Passe o tipo do erro como o segundo genérico:
const { data, error } = useSWR<User, ApiError>("/api/me", fetcher);
if (error) {
console.log(error.status); // tipado como number
}Se você omitir o genérico de erro, ele será any por padrão.
data quanto error no seu componente.isValidating para mostrar um indicador "Tentando novamente...".Revisado por Chris St. John·Última atualização: 19 de jul. de 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥