序章

エラー境界は、レンダリングフェーズ中に発生するトリッキーなエラーをキャッチする方法としてReactv16で導入されました。 以前は、これによりアプリが完全にアンマウントされ、ユーザーには空白のWebページが表示されるだけでしたが、これは理想的ではありませんでした。

この記事では、コードスニペットとインタラクティブなデモを介してエラー境界について学習します。

前提条件

この記事をフォローするには、次のものが必要です。

  • Reactにある程度精通している。 React.jsシリーズのコーディング方法を読むことができます。

このチュートリアルは、ノードv16.4.2、npm v7.19.1、およびreactv17.0.2で検証されました。

エラー境界のないエラーに遭遇する

開発中にアプリで予期しないエラーが発生することは避けられません。 存在しないオブジェクトの深くネストされたプロパティにアクセスしようとしている可能性があります。または、コントロールにない場合もあります(サードパーティAPIへのHTTPリクエストの失敗など)。

以下のデモでは、エラーをシミュレートして、エラー境界なしで通常何が起こるかを確認します。

BuggyCounter.js
import React from 'react';

class BuggyCounter extends React.Component {
  state = {
    counter: 0,
  };

  handleClick = () => {
    this.setState({
      counter: this.state.counter + 1,
    });
  };

  render() {
    if (this.state.counter === 5) {
      // Simulate an error!
      throw new Error('Simulated error.');
    }
    return (
      <div>
        <h1>{this.state.counter}</h1>
        <button onClick={this.handleClick}>+</button>
      </div>
    );
  }
}

export default BuggyCounter;

CodePenの @wle8300によるライブコード例alligatorio-react-error-boundaries-1にアクセスしてください。

+ (インクリメント)ボタンをクリックして、5でどのように失敗するかを観察します。

Output
Uncaught Error: Simulated error.

アプリでエラーが発生すると、コンポーネントは完全にアンマウントされ、ユーザーには空白のHTMLページが残ります。 これにより、ユーザーは混乱し、次に何をすべきかわからなくなる可能性があります。

エラー境界は、これらのエラーを適切に処理する方法を提供します。

エラー境界のあるエラーとの遭遇

エラー境界とは正確には何ですか? あなたが思うかもしれないことに反して、それは新しいコンポーネントやJavaScriptライブラリではありません。 これは、Reactコンポーネントのエラーを処理するための戦略のようなものです。

具体的には、Reactコンポーネントで使用できる2つのメソッドの使用法です。

MyErrorBoundaryExample.js
import React from 'react';

class MyErrorBoundaryExample extends React.Component {
  state = {
    error: null,
  };

  static getDerivedStateFromError(error) {
    // Update state so next render shows fallback UI.
    return { error: error };
  }

  componentDidCatch(error, info) {
    // Log the error to an error reporting service
    logErrorToExampleService(error, info);
  }

  render() {
    if (this.state.error) {
      // You can render any custom fallback UI
      return <p>Something broke</p>;
    }
    return this.props.children;
  }
}

export default MyErrorBoundaryExample;
  • static getDerivedStateFromErrorは、エラー境界がstateを更新し、最後のrender()をトリガーできるようにするライフサイクルメソッドです。 上記のコードスニペットでは、状態は、壊れたコンポーネント(this.props.childrenなど)ではなく、人にわかりやすいエラーメッセージを表示するために使用されています。
  • componentDidCatchは、副作用をトリガーするために設計されたライフサイクルメソッドです(たとえば、 Crashlytics などのツールにエラーを記録します)。 info.componentStackにアクセスして、バグのトリアージに役立つ開発者向けのスタックトレースを取得できます。

Reactコンポーネントは、これらのライフサイクルメソッドの少なくとも1つを採用している場合、エラー境界と見なされます。

グッドプラクティスでは、エラー処理ロジックを汎用コンポーネントに混在させるのではなく、エラー境界として専用のコンポーネントを作成することをお勧めします。

<MyErrorBoundary>を少し変更してから、<BuggyComponent>をラップして、エラーをキャッチします。

MyErrorBoundary.js
import React from 'react';

class MyErrorBoundary extends React.Component {
  state = {
    errorMessage: '',
  };

  static getDerivedStateFromError(error) {
    return { errorMessage: error.toString() };
  }

  componentDidCatch(error, info) {
    this.logErrorToServices(error.toString(), info.componentStack);
  }

  // A fake logging service.
  logErrorToServices = console.log;

  render() {
    if (this.state.errorMessage) {
      return <p>{this.state.errorMessage}</p>;
    }
    return this.props.children;
  }
}

export default MyErrorBoundary;

そしてApp.js

App.js
import React from 'react';
import BuggyCounter from './BuggyCounter';
import MyErrorBoudnary from './MyErrorBoundary';

class App extends React.Component {
  refreshPage = () => {
    history.go(0);
  };

  render() {
    return (
      <div>
        <MyErrorBoundary>
          <BuggyCounter />
        </MyErrorBoundary>
        <hr />
        <button onClick={this.refreshPage}>Refresh Page</button>
      </div>
    );
  }
}

export default App;

BuggyCounter.jsは同じままです。

CodePenの @wle8300によるライブコード例alligatorio-react-error-boundaries-2にアクセスしてください。

+ (インクリメント)ボタンをもう一度クリックしてみてください。 5に達すると、正常にクラッシュします。 さらに、コンソールを開いてスタックトレースを表示できます。

完全にクラッシュする代わりに、エラー境界を使用してフォールバックUIを置き換えることができます。 これにより、ユーザーがアプリとのやり取りを継続できるようにしながら、何かが壊れたという視覚的なフィードバックがユーザーに提供されます。

彼らは離れてナビゲートするか、顧客サービスに連絡して状況を解決することを選択できます! これは、他の点では不幸なユーザーエクスペリエンスを利用するための優れた方法です。

エラー境界とTry...Catchの比較

エラー境界は、実際にはtry...catchステートメントと直接競合していません。 エラー境界は、Reactコンポーネントの3つの場所から発生するエラーをインターセプトするためにのみ設計されています。

  • renderフェーズ中
  • ライフサイクル方式で
  • constructor

基本的に…コンポーネントのReact-y部分。

対位法として、これらはエラー境界がエラーをキャッチできない場所です。

  • イベントハンドラー(例:onClickonChangeなど)
  • setTimeoutまたはrequestAnimationFramecallbacks
  • サーバーサイドレンダリング(SSR)
  • そして、(子ではなく)エラー境界自体によって引き起こされたエラー

したがって、エラー境界はtry...catchの使用方法に実際には影響しません。 これらは両方とも、Reactでエラーを処理するための堅牢な戦略として必要です。

結論

この記事では、エラー境界について学習しました。

注:エラー境界は、クラスベースのReactコンポーネントでのみ使用できます。 この記事の執筆時点では、は現在ReactHooksを使用して実装する方法はありません。

Reactバージョン16以降、エラー境界が利用できるようになったため、通常、アプリのルートで少なくとも1つのエラー境界を使用することをお勧めします(App.jsファイルなど)。 これにより、ユーザーに空白のHTMLページが表示されなくなり、代わりに優れたフォールバックUIが表示される可能性があります。

今後は、さまざまなフォールバックUIを使用する、またはサードパーティサービスにエラーをログに記録するだけのさまざまな種類のエラー境界を使用できます。

詳細については、公式Reactドキュメントをチェックしてください!