Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Давайте признаем, что тот, кто ищет что-то в интернете, не хочет наткнуться на нерабочую пустую страницу. Это, как минимум, путает, сбивает с толку. Сидишь и не понимаешь, что произошло и почему, это оставляет плохое впечатление о сайте. Часто лучше сообщить об ошибке и дать пользователю продолжить пользоваться приложением или сайтом. В таком случае он получит меньше негативных эмоций и не закроет это приложение.
В этой статье мы пройдёмся по различным способам справиться с ошибками в приложениях на React.
Классический метод "Try and Catch" в React
Если вы использовали JavaScript, вам, вероятно, приходилось писать инструкцию "try and catch". Чтобы убедиться в этом, посмотрите:
try {
somethingBadMightHappen();
} catch (error) {
console.error("Something bad happened");
console.error(error);
Это отличный инструмент для выявления неправильного кода и обеспечения того, чтобы наше приложение не сломалось. Чтобы быть более реалистичным и максимально приближенным к миру React, давайте посмотрим пример того, как вы будете использовать это в своем приложении:
const fetchData = async () => {
try {
return await fetch("https://some-url-that-might-fail.com");
} catch (error) {
console.error(error); // You might send an exception to your error tracker like AppSignal
return error;
}
При выполнении сетевых вызовов в React обычно используют инструкцию try...catch
. Но почему? К сожалению, try...catch
работает только с императивным кодом, но не работает с декларативным, таким как JSX, который пишут в компонентах. Вот почему вы не видите массивной упаковки try...catch
всего нашего приложения. Это просто не сработает.
Итак, что делать? В React 16 появилась новая концепция — границы ошибок React. Давайте разберемся, что это такое.
Границы ошибок React
Прежде чем мы перейдем к границам ошибок, давайте сначала посмотрим, почему они необходимы. Представьте, что у вас есть такой компонент:
const CrashableComponent = (props) => {
return <span>{props.iDontExist.prop}</span>;
};
export default CrashableComponent
Если вы попытаетесь отобразить этот компонент где-нибудь, вы получите ошибку, подобную этой:
Мало того, вся страница будет пустой, и пользователь не сможет ничего делать или видеть. Но что произошло? Мы попытались получить доступ к свойству iDontExist.prop, которого не существует (мы не передаем его компоненту). Это банальный пример, но он показывает, что мы не можем поймать эти ошибки try...catch
с помощью инструкции.
Весь этот эксперимент подводит нас к границам ошибок. Границы ошибок — это компоненты React, которые улавливают ошибки JavaScript в любом месте своего дочернего дерева компонентов. Затем они регистрируют эти обнаруженные ошибки и отображают резервный пользовательский интерфейс вместо дерева компонентов, которое разбилось. Границы ошибок улавливают ошибки во время рендеринга, в методах жизненного цикла и в конструкторах всего дерева под ними.
Граница ошибки — это классовый компонент, который определяет один (или оба) из методов жизненного цикла static getDerivedStateFromError()
или componentDidCatch().
static getDerivedStateFromError()
отображает резервный пользовательский интерфейс после возникновения ошибки. componentDidCatch()
можно передавать информацию об ошибках вашему поставщику услуг (например, AppSignal) или в консоль браузера.
Вот пример того, как информация об ошибке React выглядит в "списке проблем" AppSignal:
Давайте посмотрим на типичный компонент границы ошибки:
import { Component } from "react";
class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return {
hasError: true,
error,
};
}
componentDidCatch(error, errorInfo) {
// You can also log the error to an error reporting service like AppSignal
// logErrorToMyService(error, errorInfo);
}
render() {
const { hasError, error } = this.state;
if (hasError) {
// You can render any custom fallback UI
return (
<div>
<p>Something went wrong