ErrorBoundary — великолепный инструмент для отслеживания ошибок, возникающих в компонентах React. Вы можете предоставить собственные сообщения об ошибках в зависимости от характера и места самой ошибки. Но не все возникающие ошибки обрабатываются ErrorBoundary! Что ты с ними делаешь?
При рассмотрении как асинхронных ошибок, так и ошибок, возникающих за пределами React, ErrorBoundary не соответствует действительности. Чтобы смягчить это, я в своих приложениях создал то, что я называю GlobalErrorHandler. Функциональный компонент, который: А) выводит всплывающее диалоговое окно с сообщением пользователю, что что-то пошло не так, Б) регистрирует ошибку на сервере, чтобы мы могли исследовать и найти решения.
Идея проста. Нам нужен один GlobalErrorHandler в корне нашего приложения. Этот обработчик должен обрабатывать только ошибки, не обнаруженные ErrorBoundary. Более того, пользователь должен легко закрыть его, и мы должны предположить, что приложение по-прежнему можно использовать.
Итак, стратегия такова: GlobalErrorHandler вообще ничего не делает, кроме рендеринга своих дочерних элементов по умолчанию. Но он настраивает два прослушивателя событий, которые прослушивают все события ошибок и необработанных отклонений в браузере. Затем он исследует ошибку и проверяет, была ли она уже обработана какими-либо ErrorBoundaries. Наконец, если это не так, появляется диалоговое окно, сообщающее пользователю, что где-то что-то пошло не так, и позволяет пользователю закрыть диалоговое окно и продолжить использование приложения.
Прежде чем приставать к конечным пользователям ненужными диалогами ПОВЕРХ обработки, выполняемой ErrorBoundary, мы сначала должны начать с запроса об ошибке: вас уже обработали? Мое решение — добавить новое поле в объект ошибки isHandledByBoundary. Для этого параметра установлено значение true в ErrorBoundary:
componentDidCatch(error: Error, errorInfo: ErrorInfo) { (error as any).isHandledByBoundary = true; .... }
При наличии этого во всех компонентах ErrorBoundary (и других механизмах, которые обрабатывают неперехваченные ошибки), мы готовы приступить к определению нашего GlobalErrorHandler.
Затем мы можем построить скелет нашего GlobalErrorHandler. Он напрямую отображает свои дочерние элементы, а также отображает «ErrorDialog», определенный в другом месте. (Если вы хотите использовать этот компонент совместно с приложениями, вместо него можно использовать ErrorDialog.)
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 && ( )} > ); }
Единственное, чего нам сейчас не хватает, — это самой обработки ошибок, определенной в useEffect.
Весь код в этом разделе должен находиться внутри функции useEffect!
Сначала мы определяем handleWindowError. Это сообщение должно быть доставлено обработчику событий ошибок в объекте окна. Здесь нет ничего загадочного, но обратите внимание, что событие ошибки также содержит информацию об источнике, номере строки и номере столбца. Которые могут быть ценными для коллекционирования.
Обычно эта информация также находится в объекте ошибки, но мне нужно провести дополнительные эмпирические исследования. Возможно, нам всегда следует сохранять номера строк и столбцов такими, как сообщает событие ошибки? В этом случае мы также могли бы иметь состояние для этого в GlobalErrorHandler (и убедиться, что оно отправляется при регистрации ошибки).
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; }
Мы также определим обработчик handleUnhandledRejection. Это для ошибок, возникающих в промисах, но мы забыли написать предложение .catch().
function handleUnhandledRejection(event: PromiseRejectionEvent) { setError(`Unhandled promise rejection: ${event.reason}`); setDialogOpen(true); }
Тогда все, что нам нужно сделать, это настроить прослушиватели и удалять их всякий раз, когда GlobalErrorHandler больше не отображается:
window.addEventListener('error', handleWindowError); window.addEventListener('unhandledrejection', handleUnhandledRejection); return () => { window.removeEventListener('error', handleWindowError); window.removeEventListener( 'unhandledrejection', handleUnhandledRejection ); };
Конечно, операторы return — это то место, где мы возвращаемся из функции, которую передаем useEffect. Это гарантирует, что мы начинаем прослушивать события и обрабатывать их, когда компонент визуализируется, и останавливаемся, когда компонент больше не отображается.
Таким образом, у нас есть GlobalEventHandler для обработки тех неприятных ошибок в нашем React-приложении, которые либо выдаются из асинхронных источников, либо выдаются за пределами компонентов React!
Отказ от ответственности: Все предоставленные ресурсы частично взяты из Интернета. В случае нарушения ваших авторских прав или других прав и интересов, пожалуйста, объясните подробные причины и предоставьте доказательства авторских прав или прав и интересов, а затем отправьте их по электронной почте: [email protected]. Мы сделаем это за вас как можно скорее.
Copyright© 2022 湘ICP备2022001581号-3