Устойчивая архитектура в React: использование Error Boundaries
Недавно я реализовал программу чтения PDF-файлов для журнала на странице, протестировал ее в настольном браузере, и все работало отлично.
Проблема возникла на мобильном телефоне, поскольку после просмотра нескольких страниц pdf сайт полностью завис: «Ошибка приложения: произошло исключение на стороне клиента (дополнительную информацию см. в консоли браузера)».
Я не буду говорить о решении, а о том, как предотвратить полный сбой вашего приложения в этих случаях, используя концепцию Error Boundary для предоставления альтернативного пользовательского интерфейса в случае ошибки.
До React 16 ошибка в любом компоненте могла привести к сбою всего приложения, поскольку ошибка распространялась (намеренно, это не ошибка, это функция🫣), полностью нарушая работу сайта и создавая плохой пользовательский опыт. С появлением React 16 были введены границы ошибок, позволяющие разработчикам фиксировать и обрабатывать ошибки в конкретных компонентах, не выводя из строя все приложение.
Что такое границы ошибок?
Error Boundaries — это компонент React, который перехватывает ошибки JavaScript в своих дочерних компонентах, регистрирует эти ошибки и отображает резервный интерфейс вместо отказавшего компонента, работая как catch
для компонентов.
В настоящее время он доступен только как компонент класса, и пока нет никаких свидетельств того, что команда React хочет это изменить (хорошо, официально этого нет, но вы найдете способы сделать это как вручную, так и с помощью внешних библиотек и об этом я расскажу позже).
Пример кода:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, info) {
logErrorToService(error, info.componentStack);
}
render() {
if (this.state.hasError) {
return <h1>Something has gone wrong.</h1>;
}
return this.props.children;
}
}
Давайте лучше поймем роль функций:
- getDerivedStateFromError: эта статическая функция используется для обновления локального состояния компонента и отрисовки резервного пользовательского интерфейса после ошибки.
- componentDidCatch: этот метод запускается после того, как Error Boundary перехватывает ошибку. Это полезно для регистрации ошибок или побочных эффектов.
При использовании Error Boundary в своем приложении оберните компоненты, которые хотите защитить:
<ErrorBoundary>
<Component />
</ErrorBoundary>
Вы также можете задействовать все приложение или содержимое сайта, исключая макет или даже только маршруты, все зависит от выбранного вами дизайна и того, который имеет наибольший смысл в вашем контексте.
Когда использовать, а когда не использовать
Когда использовать
- В некритических компонентах или функциях.
- При включении виджетов или сторонних библиотек.
Когда не использовать
- Для ожидаемых ошибок (например, известных ошибок API).
- В обработчиках событий.
- Для ошибок, которые следует обрабатывать конкретно, а не в общем.
- Асинхронный код: Error Boundaries не улавливают ошибки в асинхронных операциях за пределами компонентов React, таких как
setTimeout
илиrequestAnimationFrame
. Для обработки ошибок в асинхронных операциях внутри компонентов необходимо использоватьtry/catch
блоки. - Серверный рендеринг
- Ошибки, возникающие в самой Error Boundaries.
Дополнительно:
- Интегрируйте границы ошибок с решениями для мониторинга ошибок для быстрого выявления и исправления. Вы можете интегрировать их, например, с Sentry или Exceptionless.
- Проверьте свои Error Boundaries, чтобы убедиться, что они работают должным образом в различных сценариях.
Сторонние библиотеки: react-error-boundary
Хотя встроенных границ ошибок React достаточно для удовлетворения этих потребностей, некоторые сторонние библиотеки были разработаны для упрощения и обогащения этой функциональности. Одним из самых популярных является react-error-boundary
.
Основные преимущества
- Упрощенный API: с помощью
react-error-boundary
вы можете создать границу ошибки с помощью простой резервной функции без необходимости создания класса компонента, и он предлагает несколько других возможностей, в которые я не буду сегодня вдаваться.
Пример с react-error-boundary
:
import { ErrorBoundary } from 'react-error-boundary'
function FallbackComponent({ error }) {
return <div>An error has occurred: {error.message}</div>
}
<ErrorBoundary FallbackComponent={FallbackComponent}>
<Component />
</ErrorBoundary>
- Дополнительные перехватчики и утилиты: библиотека предлагает
useErrorHandler
перехватчик, который позволяет перехватывать и обрабатывать ошибки в функциональных компонентах и пользовательских функциях. - Автоматический перезапуск: с помощью этого
onReset
свойства вы можете определить, как компонент должен перезапуститься после ошибки.