Поиск по сайту:

Как использовать границы ошибок React для обнаружения сбоев


Границы ошибок React позволяют выявлять ошибки JavaScript, возникающие в дочерних компонентах. Любая необработанная ошибка, возникающая ниже положения дерева границы, будет перехвачена, что предотвратит сбой.

Вы можете отобразить собственный резервный пользовательский интерфейс после того, как граница ошибки перехватит ошибку. Это позволяет изящно сообщить пользователю о проблеме. Они смогут продолжать использовать остальную часть вашего интерфейса, не страдая от полностью разбитой вкладки.

Создание границ ошибок

Любой компонент класса React может стать границей ошибки. Вам просто нужно установить один из следующих методов жизненного цикла:

  • componentDidCatch(error) — этот метод экземпляра будет вызываться всякий раз, когда компонент перехватывает ошибку. Вы можете использовать это, чтобы сообщить об ошибке в службу аналитики или мониторинга.
  • static getDerivedStateFromError(error) — этот статический метод можно использовать для обновления состояния вашего компонента после возникновения ошибки. Вот как вы отображаете резервный пользовательский интерфейс.

Вот как эти два метода выглядят при использовании:

componentDidCatch()

class MyComponent extends React.Component {
 
    componentDidCatch(error) {
        // use custom reporting framework
        logErrorToAnalytics(error);
        this.props.onError(error);
    }
 
}

Используя componentDidCatch(), ваш компонент может сообщить об ошибке любым удобным для него способом. Поскольку это метод экземпляра, вы также можете передать его вверх по дереву компонентов через свойства.

getDerivedStateFromError(ошибка)

class MyComponent extends React.Component {
 
    state = {error: null};
 
    render() {
        return <h1>{!this.state.error ? "Hello" : "Error"}</h1>;
    }
 
    static getDerivedStateFromError(error) {
        return {error};
    }
 
}

getDerivedStateFromError() также получает объект ошибки JavaScript. Он должен возвращать объект, описывающий преобразование состояния для применения к вашему компоненту.

React передаст возвращенный объект в setState(). В этом примере значение ключа error в состоянии компонента будет установлено в объект пойманной ошибки. Это приведет к изменению отображаемого вывода на Error вместо стандартного текста Hello.

Какой метод использовать?

Если два граничных метода кажутся похожими, это потому, что они таковыми являются! Технически, вы можете определить любой из этих методов или оба и получить одинаковые результаты — componentDidCatch() может вызвать setState() для обновления состояния вашего компонента, а getDerivedStateFromError() может вызвать внешнюю службу мониторинга, чтобы сообщить об обнаруженных ею ошибках.

Разница заключается в фазе, на которой обнаружена ошибка. componentDidCatch() фиксирует ошибки на этапе фиксации после того, как React обновил DOM. getDerivedStateFromError() будет вызываться во время фазы рендеринга, прежде чем React обновит DOM браузера.

Эта временная тонкость объясняет, почему getDerivedStateFromError() обычно используется для переключения на резервный пользовательский интерфейс. Когда возникает серьезная ошибка, акт обновления DOM может спровоцировать дальнейшие ошибки, если ваше приложение осталось в несогласованном состоянии. Обновление состояния перед обновлением DOM обеспечивает немедленную визуализацию резервного пользовательского интерфейса.

Определение границ вашей ошибки

Вы можете использовать границы ошибок везде, где считаете нужным. Хорошей практикой является использование нескольких границ ошибок. Добавьте границу ошибки для каждого основного уровня вашего пользовательского интерфейса. Это позволяет вам изолировать ошибки в содержимом вашей страницы от оболочки приложения, поэтому сбой на маршруте не отключает вашу панель навигации.

Вот простая иерархия компонентов:

export const () => (
    <App>
        <Header />
        <Router />
        <Footer />
    </App>
);

В этом приложении компонент App представляет собой простую оболочку, управляющую состоянием верхнего уровня. Header отображает панель навигации, а Footer отображает нижнюю панель. Содержимое главной страницы, где наиболее вероятны сбои, загружается динамически с помощью Маршрутизатора на основе текущего URL-адреса.

По умолчанию сбой в дочернем элементе Маршрутизатор отключает весь сайт. Поместив границу ошибки вокруг Маршрутизатора, ошибки, возникающие внутри компонента, могут быть корректно обработаны. Верхний и нижний колонтитулы остаются пригодными для использования, а основное содержимое страницы заменяется резервным сообщением.

Приложению требуется как минимум еще одна граница ошибки. Обертывание дочерних элементов App гарантирует, что ошибки, возникающие в верхнем или нижнем колонтитуле, могут быть обнаружены. В этой ситуации может быть приемлемо заменить весь пользовательский интерфейс полностраничным сообщением об ошибке.

Вот измененная структура компонента:

class ErrorBoundary extends React.Component {
 
    state = {error: null};
 
    render() {
        if (!this.state.error) return this.props.children;
        else return <h1>Error!</h1>;
    }
 
    static getDerivedStateFromError(error) {
        return {error};
    }
 
}
 
export const () => (
    <App>
        <ErrorBoundary>
            <Header>
            <ErrorBoundary>
                <Router />
            </ErrorBoundary>
            <Footer />
        </ErrorBoundary>
    </App>
);

Мы абстрагировали логику границы ошибки в повторно используемый компонент. Теперь мы можем обернуть ErrorBoundary любые компоненты, которые должны быть изолированы от своих родителей. Помните, что вам не нужно создавать компонент границы ошибки — для простых приложений или определенного компонента пользовательского интерфейса вы можете добавить перехватчики жизненного цикла непосредственно в класс компонента.

Ограничения границ погрешности

Границы ошибок имеют некоторые важные ограничения, о которых вам следует знать. Они способны обнаруживать большинство необработанных ошибок JavaScript, но некоторые из них остаются незамеченными.

Границы ошибок не будут перехватывать ошибки, возникающие в методах обработчиков событий. Код обработчика событий не влияет на процесс рендеринга React, поэтому фреймворк по-прежнему может отображать ваши компоненты. Поскольку ошибки обработчика событий не приведут к повреждению пользовательского интерфейса или отключению компонента, React не пытается их перехватить.

Если вам нужно реагировать на ошибки в ваших обработчиках событий, вы должны использовать обычный блок try/catch. Выполните обновление состояния в операторе catch, чтобы переключить ваш пользовательский интерфейс в состояние ошибки.

class MyComponent extends React.Component {
 
    state = {error: null};
 
    handleClick = () => {
        try {
            doSomething();
        }
        catch (error) {
            this.setState({error});
        }
    }
 
    render() {
        if (this.state.error) return <p>Error!</p>;
        else return <button onClick={this.handleClick}>Submit</button>
    }
 
}

Помимо обработчиков событий, границы ошибок не могут обнаруживать ошибки, возникающие в асинхронном коде. Если вы используете промисы, async/await или setTimeout(), убедитесь, что вы используете try/catch/Promise.catch() блокирует любые ошибки, которые могут возникнуть.

Распространенное непонимание границ ошибок касается дерева, которое они отслеживают. Они могут только перехватывать ошибки, которые возникают глубже в дереве. Границы ошибок не будут улавливать ошибки, вызванные самим граничным компонентом.

export default () => (
    <App>                   // Errors won't be caught
        <ErrorBoundary>     // Errors won't be caught
            <Router />      // Errors thrown here will be caught
        </ErrorBoundary>
    </App>
);

Каждая граница ошибки должна охватывать компоненты, которые могут вызвать ошибку.

Наконец, только компоненты на основе классов могут быть границами ошибок. В настоящее время нет механизма, позволяющего функциональному компоненту стать границей ошибки. Если вы работаете с функциональной кодовой базой, вам следует создать многократно используемый компонент границы ошибок, как показано выше. Затем вы можете обертывать свои компоненты им каждый раз, когда вам нужна граница ошибки.

Заключение

Границы ошибок переносят try/catch JavaScript в модель декларативного рендеринга React. Они позволяют вам изолировать части пользовательского интерфейса вашего сайта, поэтому сбой в одном компоненте не повлияет на его братьев и сестер.

Вы должны оценить пользовательский интерфейс вашего приложения, чтобы определить критические разделы. Стратегически размещайте границы ошибок, чтобы необработанная ошибка не размонтировала все дерево компонентов. Пользователи с гораздо большей вероятностью примут полностью стилизованное сообщение «что-то пошло не так», чем белый экран, который нужно обновить.




Все права защищены. © Linux-Console.net • 2019-2024