Error Boundaries y recuperación en React | Nicolás Garzón
React todavía requiere un componente de clase para implementar una boundary directamente:
TypeScript
Copiar class ErrorBoundary extends React . Component< Props, State> {
state = { hasError: false } ;
static getDerivedStateFromError ( ) {
return { hasError: true } ;
}
componentDidCatch ( error: Error, info: React. ErrorInfo) {
reportError ( error, info) ;
}
render ( ) {
if ( this . state. hasError) return this . props. fallback;
return this . props. children;
}
}
Errores dentro de handlers.
Código asíncrono fuera del render.
Errores de la propia boundary.
Coloca boundaries alrededor de unidades que pueden fallar de forma independiente: contenido principal, editor, panel de datos o ruta.
Cambiar una key puede crear una instancia limpia después de reintentar.
Suspense maneja espera; Error Boundary maneja fallo. Suelen combinarse.
Una sola boundary global sin recuperación local.
Mostrar detalles técnicos sensibles al usuario.
Atrapar errores y no registrarlos.
Usarla como sustituto de validación normal.
Una boundary limita el radio de daño y permite que el resto de la aplicación continúe funcionando.
Una boundary captura errores de descendientes durante render y fases de React compatibles. La propia boundary permanece disponible para mostrar fallback.
Texto
Copiar App
├─ Header
└─ ErrorBoundary
└─ AnalyticsWidget ✕
↓
WidgetFallbackEl Header continúa funcionando. La granularidad define el radio de daño.
TypeScript
Copiar class ErrorBoundary extends React . Component<
{ fallback: React. ReactNode; children: React. ReactNode; onError? : ( error: Error, info: React. ErrorInfo) => void } ,
{ hasError: boolean }
> {
state = { hasError: false } ;
static getDerivedStateFromError ( ) {
return { hasError: true } ;
}
componentDidCatch ( error: Error, info: React. ErrorInfo) {
this . props. onError?. ( error, info) ;
}
render ( ) {
if ( this . state. hasError) return this . props. fallback;
return this . props. children;
}
} La API base sigue usando class component. Frameworks y librerías pueden ofrecer wrappers, hooks auxiliares o boundaries por ruta.
Error dentro de un click handler.
Promise rechazada iniciada fuera del render.
Error del propio fallback.
Error durante server rendering bajo algunos flujos.
Código externo que React no ejecuta.
Handlers deben usar try/catch cuando pueden recuperarse localmente:
TypeScript
Copiar async function handleSave ( ) {
try {
await save ( ) ;
} catch ( error) {
setMessage ( toUserMessage ( error) ) ;
}
} Texto
Copiar Promise pendiente → Suspense fallback
Promise rechazada → Error Boundary
error de render → Error BoundaryTypeScript
Copiar < ErrorBoundary fallback= { < ReportsError / > } >
< Suspense fallback= { < ReportsSkeleton / > } >
< Reports / >
< / Suspense>
< / ErrorBoundary> Una boundary que quedó en hasError necesita una transición para reintentar. Opciones:
Botón que reinicia su state.
Cambiar key de la boundary.
Navegar a otra ruta.
Librería que implemente resetKeys.
TypeScript
Copiar < ErrorBoundary key= { reportId} fallback= { < ReportError / > } >
< Report id= { reportId} / >
< / ErrorBoundary> Cambiar entidad crea una boundary limpia. Para reintentar la misma, expón un reset controlado.
Explica qué sección falló.
Conserva navegación y contexto.
Ofrece reintentar o volver.
No muestra stack trace.
Tiene semántica accesible.
Evita perder datos editados si pueden recuperarse.
TypeScript
Copiar < section role= "alert" aria- labelledby= "widget-error-title" >
< h2 id= "widget-error-title" > No pudimos cargar el reporte< / h2>
< button type= "button" onClick= { retry} > Intentar de nuevo< / button>
< / section> componentDidCatch recibe componentStack, útil para saber qué subárbol falló. Registra:
Release.
Ruta.
Identificador de boundary.
Error y component stack.
Contexto no sensible.
No incluyas tokens, formularios completos o datos personales innecesarios.
Boundary global: evita pantalla blanca, pero fallback de toda la app puede ser brusco.
Boundary por ruta: buena para pantallas independientes.
Boundary por widget: permite que dashboard continúe.
Demasiadas boundaries también añaden ruido y pueden ocultar errores repetidos. Colócalas donde existe una estrategia real de recuperación.
No lances errores para cualquier estado normal:
Lista vacía.
Validación de formulario.
404 esperado.
Permiso denegado que el producto sabe representar.
Modela esos casos como datos o rutas. Usa boundary para fallos que impiden renderizar la región con normalidad.
Si el subárbol se desmonta al fallar, su state puede perderse. Para editores importantes:
Guarda borrador fuera de la región frágil.
Persiste periódicamente.
Coloca boundary alrededor de la parte riesgosa, no de todo el formulario.
Ofrece recuperación.
Frameworks pueden manejar errores de servidor mediante archivos o boundaries de ruta. No asumas que una class boundary cliente captura todo error del servidor. Documenta la capa:
Render servidor.
RSC.
Hydration.
Cliente posterior.
Crea un componente que lance:
TypeScript
Copiar function Broken ( ) {
throw new Error ( "boom" ) ;
} Verifica fallback, logging y reset. Suprime el ruido esperado de consola de forma localizada, no global. Prueba también errores en handlers por separado porque no los captura la boundary.
Una Error Boundary limita el daño de errores de render descendientes.
No captura handlers ni toda asincronía.
Suspense y Error Boundary resuelven pending y fallo respectivamente.
El fallback necesita recuperación y observabilidad.
La granularidad debe corresponder a una unidad independiente.
¿Por qué una boundary no captura el error de un click handler?
¿Cómo reintentas una boundary?
¿Qué estados no deberían lanzarse como error?
¿Qué información registrarías sin exponer datos sensibles?
Ver respuestas
Porque el handler ocurre fuera de la fase de render protegida.
Reiniciando su state o cambiando deliberadamente su key.
Vacío, validación, 404 o permiso esperado que la UI puede modelar.
Error, component stack, release, ruta y boundary afectada.
Datos remotos, caché y server state distingue errores de render de estados normales de una fuente remota.