ErrorBoundary es una magnífica herramienta para capturar errores generados por los componentes de React. Puede proporcionar mensajes de error personalizados según la naturaleza y ubicación del error en sí. ¡Pero no todos los errores son manejados por ErrorBoundary! ¿Qué haces con esos?
Al considerar tanto los errores asíncronos como los errores generados desde fuera de React, el ErrorBoundary se queda corto. Para mitigar esto, en mis aplicaciones he creado lo que llamo GlobalErrorHandler. Un componente funcional que simplemente A) muestra un cuadro de diálogo de error que le indica al usuario que algo salió mal, B) registra el error en el servidor, para que podamos investigar y encontrar soluciones.
La idea es simple. Queremos un GlobalErrorHandler en la raíz de nuestra aplicación. Este controlador debe solo manejar errores no detectados por ErrorBoundary. Es más, el usuario debería descartarlo fácilmente y deberíamos asumir que la aplicación aún se puede utilizar.
Entonces la estrategia es la siguiente: GlobalErrorHandler no hace nada en absoluto, excepto representar a sus hijos, de forma predeterminada. Pero configura dos detectores de eventos, que escuchan todos los errores y eventos de rechazo no controlados en el navegador. Luego examina el error y comprueba si ya ha sido manejado por algún ErrorBoundaries. Finalmente, si ese no es el caso, aparece un cuadro de diálogo que le informa al usuario que algo salió mal en alguna parte y le permite cerrar el cuadro de diálogo y continuar usando la aplicación.
Antes de molestar a los usuarios finales con cuadros de diálogo innecesarios ADEMÁS del manejo realizado por ErrorBoundary, primero debemos comenzar preguntando el error: ¿Ya lo manejaron? Mi solución a esto es introducir un nuevo campo en el objeto de error isHandledByBoundary. Esto se establece en verdadero dentro de ErrorBoundary:
componentDidCatch(error: Error, errorInfo: ErrorInfo) { (error as any).isHandledByBoundary = true; .... }
Con esto implementado en todos los componentes de ErrorBoundary (y otra maquinaria que maneja errores no detectados), estamos listos para comenzar a definir nuestro GlobalErrorHandler.
Luego podemos construir el esqueleto de nuestro GlobalErrorHandler. Representa directamente a sus hijos y también presenta un "ErrorDialog" definido en otro lugar. (Si desea compartir este componente entre aplicaciones, ErrorDialog podría ser un accesorio).
import { useState, useEffect, ReactNode } from 'react'; import { ErrorDialog } from '../Components/ErrorDialog'; type Props = { children: ReactNode; }; export function GlobalErrorHandler({ children }: Props) { const [error, setError] = useState(null); const [isDialogOpen, setDialogOpen] = useState(false); useEffect(() => { .... }, []); function handleCloseDialog() { setDialogOpen(false); setError(null); } return ( {children} {isDialogOpen && error && ( )} > ); }
Lo único que nos falta ahora es el manejo de errores en sí, definido dentro de useEffect.
¡Todo el código de esta sección debe ubicarse dentro de la función useEffect!
Primero definimos handleWindowError. Esto debe entregarse al controlador de eventos de error en el objeto de ventana. No hay nada misterioso aquí, pero observe que el evento de error también contiene información sobre la fuente, el número de línea y el número de columna. Que podría ser valioso coleccionar.
Por lo general, esta información también se encuentra dentro del objeto de error, pero necesito realizar más investigaciones empíricas al respecto. ¿Quizás siempre deberíamos mantener los números de línea y columna tal como lo informa el evento de error? En ese caso, también podríamos tener un estado para esto dentro de GlobalErrorHandler (y asegurarnos de que se envíe al registrar el error).
function handleWindowError( message: string | Event, source?: string, lineno?: number, colno?: number, error?: Error ) { if (error && (error as any).isHandledByBoundary) { return true; } const errorMessage = error ? error : `Error: ${message} at ${source}:${lineno}:${colno}`; setError(errorMessage); setDialogOpen(true); return true; }
También definiremos el controlador handleUnhandledRejection. Esto es para errores que surgen dentro de las promesas, pero en los que olvidamos escribir una cláusula .catch().
function handleUnhandledRejection(event: PromiseRejectionEvent) { setError(`Unhandled promise rejection: ${event.reason}`); setDialogOpen(true); }
Entonces todo lo que tenemos que hacer es configurar los oyentes y eliminarlos cada vez que GlobalErrorHandler ya no se procese:
window.addEventListener('error', handleWindowError); window.addEventListener('unhandledrejection', handleUnhandledRejection); return () => { window.removeEventListener('error', handleWindowError); window.removeEventListener( 'unhandledrejection', handleUnhandledRejection ); };
Las declaraciones de retorno es, por supuesto, donde regresamos de la función que estamos alimentando useEffect. Esto garantiza que comenzamos a escuchar los eventos y los manejamos cuando el componente se procesa, y nos detenemos cuando el componente ya no se procesa.
Por lo tanto, tenemos un GlobalEventHandler, para manejar esos molestos errores en nuestra aplicación React que se generan desde fuentes asincrónicas o desde fuera de los componentes de React.
Descargo de responsabilidad: Todos los recursos proporcionados provienen en parte de Internet. Si existe alguna infracción de sus derechos de autor u otros derechos e intereses, explique los motivos detallados y proporcione pruebas de los derechos de autor o derechos e intereses y luego envíelos al correo electrónico: [email protected]. Lo manejaremos por usted lo antes posible.
Copyright© 2022 湘ICP备2022001581号-3