Перехват ошибок в компоненте React

Поговорим о механизме перехвата ошибок из компонента и отображении резервного компонента («ErrorComponent») в случае возникновения ошибки при отображении компонента.

В статье речь пойдёт о следующих событиях жизненного цикла:

  1. Работа с «componentDidCatch».
  2. Использование события жизненного цикла «getDerivedStateFromError».

Сценарий варианта использования

  1. Создаём компонент «EmployeeDetails», который содержит поле ввода.
  2. Если пользователь оставляет в поле ввода пробел, компонент должен вернуть ошибку: пробелы не допустимы.
  3. Развёртываем механизм, который отслеживает возвращаемую ошибку.
  4. При появлении ошибки компонент должен отображать «ErrorComponent», а не «EmployeeDetails».

Приступаем к работе с компонентом «EmployeeDetails»

Сначала создадим компонент «EmployeeDetails», который выдаёт ошибку, если поле ввода содержит пробел.

export class EmployeeDetails extends React.Component {
  
  updateName(event) {
    if(event.target.value.indexOf(" ") > -1) {
      throw "Name Cannot Contain blank Spaces";
    }
  }
  render() {
    return (
      <div>
        Enter Name: <input type="text" placeholder="Enter Name" onChange={this.updateName} />
      </div>
    )
  }
}

Каждый раз, когда мы вносим какие-то изменения в поле ввода, в этом компоненте вызывается функция «updateName». Она проверяет текущее обновлённое значение текстового поля на предмет наличия пробелов. Если пробелы имеются, компонент возвращает ошибку. Обработки ошибки не происходит, поэтому приложение завершит работу с ошибкой «Name Cannot Contain blank Spaces» («Имя не может содержать пробелы»).

Добавление механизма отслеживания ошибок с помощью «componentDidCatch»

Для перехвата ошибки введём понятие «граница ошибки». Создадим компонент, выступающий в роли границы, для перехвата ошибок, которые будут вызваны всеми его дочерними компонентами.

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

Теперь создадим компонент, который можно представить как «ErrorBoundaries»:

import React from 'react';

export default class ErrorBoundaries extends React.Component {

    componentDidCatch(error, info) {
        console.log("Component Did Catch Error");
    }

    render() {
        return (
            <div>
                <EmployeeDetails />
            </div>
        )
    }
}

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

Вернёмся к компоненту «ErrorComponent»

В том случае, когда компонент возвращает ошибку, он должен отображать «ErrorComponent». Этот компонент содержит заголовок, по которому можно понять, что пошло не так при использовании «EmployeeDetails». Чтобы развернуть этот резервный механизм во время появления ошибки, можно реализовать событие жизненного цикла «getDerivedStateFromError».

Понятие жизненного цикла «getDerivedStateFromError»

Это событие жизненного цикла можно использовать для определения нового состояния после получения ошибки. Как только ошибка получена, будет выполнено следующее событие жизненного цикла, что может обновить состояние показанного выше компонента «ErrorBoundaries». Это состояние можно настроить так, чтобы оно давало понять, как выглядит ситуация ошибки в приложении. После обновления состояния компонент «ErrorBoundaries» будет повторно отображён. На основе состояния компонента можно будет развернуть «ErrorComponent».

Расширим компонент «ErrorBoundaries» и реализуем вот это:

import React from 'react';

export default class App extends React.Component {

  constructor() {
    super();
    this.state = {
      hasError: false
    }
  }

  componentDidCatch(error, info) {
      console.log("Component Did Catch Error");
  }
  
  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

    render() {
        return (
          <div>
            { !this.state.hasError && <EmployeeDetails /> }
            { this.state.hasError && <ErrorComponent /> }
          </div>
        )
    }
}

В приведённом выше компоненте мы создали переменную состояния «hasError». Её можно использовать для отслеживания ошибки внутри дочерних компонентов. В начальной фазе никаких ошибок нет, поэтому исходным значением «hasError» будет «false». Как только в компоненте «EmployeeDetails» появляется ошибка, вызывается функция «getDerivedStateFromError», которая устанавливает для «hasError» значение «true».

Компонент настраивает новое состояние, поэтому он будет отображён повторно. В функции «render» мы указали условие, которое определяет, что будет отображаться: «EmployeeDetails» или «ErrorComponent». Так как для «hasError» установлено значение «true», отображён будет «ErrorComponent». В этом сценарии ошибка обработана, поэтому приложению ничто не мешает продолжать работу. Код доступен здесь.

** Чтобы продемонстрировать требуемое поведение кода, он должен запускаться в режиме «production». Приведённый выше код дан для примера, он не запускается в режиме «production».

Читайте также:

Читайте нас в Telegram, VK и Яндекс.Дзен


Перевод статьи Mayank Gupta: Capturing Errors in React Component