Обработка ошибок в Javascript несложна, но когда мы пишем компоненты React, у нас могут возникнуть трудности с устранением неперехваченных ошибок, потому что try / catch работает только для императивного кода, а компоненты React декларативны.

Начиная с ранней разработки React (≤15), дочерний компонент часто выдавал ошибку, чтобы сломать все приложение, и React не предоставлял способ изящно обрабатывать их в компонентах. Вот почему границы ошибок - это хорошо.

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

В React 16 у нас есть встроенная функция под названием componentDidCatch для работы с этими неперехваченными ошибками. Давайте взглянем:

Это компонент, который выдает ошибку, когда счетчик достигает 5.

class BuggyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { counter: 0 };
    this.handleClick = this.handleClick.bind(this);
  }
  
  handleClick() {
    this.setState(({counter}) => ({
      counter: counter + 1
    }));
  }
  
  render() {
    if (this.state.counter === 5) {
      // Simulate an error
      throw new Error('Opss!');
    }
    return <button onClick={this.handleClick}>{this.state.counter}</button>;
  }
}

Это компонент ErrorBoundary, в котором мы можем использовать метод componentDidCatch.

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { error: null, errorInfo: null };
  }
  
  componentDidCatch(error, errorInfo) {
   this.setState({
      error: error,
      errorInfo: errorInfo
    });
  }
  
  render() {
    if (this.state.errorInfo) {
      return (
        <div>
          <h2>Something went wrong</h2>
          <details style={{ whiteSpace: 'pre-wrap' }}>
            {this.state.error && this.state.error.toString()}
            <br />
            {this.state.errorInfo.componentStack}
          </details>
        </div>
      );
    }
    // Render children if there's no error
    return this.props.children;
  }  
}

App.js

function App() {
  return (
    <div>
      <p>
        <b>
          This is an example of error boundaries in React 16.x
          <br /><br />
        </b>
      </p>
      <hr />
      <ErrorBoundary>
        <p>These components are inside the same error boundary. If one crashes, the error boundary will replace both of them.</p>
        <BuggyComponent />
        <BuggyComponent />
      </ErrorBoundary>
      <hr />
      <p>These two counters are each inside of their own error boundary. So if one crashes, the other is not affected.</p>
      <ErrorBoundary><BuggyComponent /></ErrorBoundary>
      <ErrorBoundary><BuggyComponent /></ErrorBoundary>
    </div>
  );
}
ReactDOM.render(
  <App />,
  document.getElementById('root')
);

Если вы запустите это приложение, вы увидите следующее:

Теперь, нажав на кнопки более 5 раз, вы увидите ошибки. Для первых двух кнопок, если одна из них выйдет из строя, граница ошибки заменит их обе, потому что они находятся внутри одной границы ошибки. Для двух других кнопок каждая из них находится внутри своей границы ошибки. Так что если один выйдет из строя, другой не пострадает.

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

Это все, ребята,

Спасибо за прочтение!