【React】ErrorBoundary × Bugsnagによるエラー対応

こんにちは。スタメン エンジニアのミツモトです。 スタメンで開発しているサービス TUNAG では、JSのフレームワークとして部分的にReactを採用しています。 最近、Reactのバージョンアップが行われ、それに伴いエラー対応を行ったので、 今回はその事についてご紹介させていただきます。

目次

はじめに

Reactのバージョンをv15.4.2 → v16.8.6 に上げたことで、エラーが発生した場合の表示が変わりました。 v15では、描画されたコンポーネントはエラーが発生しても画面が変わらなかったのが、v16だとコンポーネントのツリー全体がアンマウントされる(表示されなくなる)ようになりました。

壊れたコンポーネントがアンマウントされるのは良いのですが、このままだと、サービスを利用するユーザーにとっては、「何が起きたんだ?」となり、UXとして良くありません。そこで登場するのがErrorBoundaryです。

ErrorBoundary

公式

自身の子コンポーネントツリーで発生した JavaScript エラーをキャッチし、エラーを記録し、クラッシュしたコンポーネントツリーの代わりにフォールバック用の UI を表示する React コンポーネント
class ErrorBoundary extends React.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 };
  }

  componentDidCatch(error, errorInfo) {
    // You can also log the error to an error reporting service
    logErrorToMyService(error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return (
        <div>
          アプリケーションエラーにより表示できません。時間を置いて、再度更新してください。
        </div>
      )
    }
    return this.props.children; 
  }
}
// エラーハンドリングしたい他のコンポーネントを囲う
<ErrorBoundary>
  <MyWidget />
</ErrorBoundary>

getDerivedStateFromError と componentDidCatch は、コンポーネントのレンダー中、ライフサイクルメソッド内、コンストラクタ内でエラーが発生したときに呼び出されます。 上記のコードでは、getDerivedStateFromErrorにより、 hasError の state を切り替えて代わりとなるコンポーネントを表示し、 componentDidCatch によってエラー情報を記録しています。 囲ったコンポーネントだけでなく、配下のコンポーネントのエラーもキャッチします。

ErrorBoundary を使うと、以下のように代わりのコンポーネントが表示されます。

ErrorBoundary × Bugsnag

当初は、公式のようにErrorBoundaryを実装していました。スタメンでは、TUNAGのエラー監視に Bugsnag というサービスを採用しているのですが、Bugsnag が ErrorBoundary を扱える パッケージ を提供していることを知り、エラーの通知含めシンプルに実装できるため、そちらに変更しました。

必要なパッケージ

  • dotenv
  • @bugsnag/js
  • @bugsnag/plugin-react

実装

まずは、 Bugsnag のAPIキーを環境変数として定義します。

// .env
BUGSNAG_API_KEY=*****************************(取得したBugsnagのAPIキーを入力)

webpack の DefinePlugin により、 process.env.BUGSNAG_API_KEY で、環境変数APIキーを呼び出せるようにします。

// webpack.config.js
// 抜粋
const webpack = require('webpack');
require('dotenv').config();
<200b>
module.exports = () => {
  return ({
    plugins: [
      new webpack.DefinePlugin({
        'process.env': {
          'BUGSNAG_API_KEY': JSON.stringify(process.env.BUGSNAG_API_KEY),
        }
      }),
    ],
  });
};

bugsnagClient、ErrorBoundary、エラー発生時に代わりに表示するコンポーネント( DefaultFallbackComponent )を用意します

// src/utilities/bugsnag/bugsnagClient.js
import bugsnag from '@bugsnag/js';

const bugsnagClient = bugsnag(`${process.env.BUGSNAG_API_KEY}`);

export default bugsnagClient;
// src/utilities/ErrorBoundary.js
import React from 'react';
import bugsnagReact from '@bugsnag/plugin-react';
import bugsnagClient from 'utilities/bugsnag/bugsnagClient';

bugsnagClient.use(bugsnagReact, React);
const ErrorBoundary = bugsnagClient.getPlugin('react');

export default ErrorBoundary;

export const DefaultFallbackComponent = () => {
  return (
    <div>
      アプリケーションエラーにより表示できません。時間を置いて、再度更新してください。
    </div>
  );
};

最後に、エラーをキャッチしたいコンポーネントを囲います。その際、 ErrorBoundary の FallbackComponent プロパティに、代わりに表示するコンポーネントを渡します。

  import ErrorBoundary, { DefaultFallbackComponent } from 'src/utilities/ErrorBoundary';

  <ErrorBoundary
    FallbackComponent={DefaultFallbackComponent}
  >
    <MyComponent />
  </ErrorBoundary>

以上で、ユーザーに対して適切なエラー画面を表示でき、同時にエラーを通知できます。

おまけ(Bugsnagのエラー通知にユーザー情報を付与)

bugsnagClientにユーザー情報を渡すことで、Bugsnagのエラー通知にユーザー情報を含められます。

bugsnagClient.user = { 
  companyId: 1,
  userId: 1,
  name: 'テスト 太郎',
};

Bugsnagの画面

4. おわりに

ErrorBoundary × Bugsnag による React のエラー対応についてご紹介しました。 React をバージョンアップしたことで、新たな APIやライブラリを使うことができるようになったので、これから新機能を開発するのが楽しみです。

スタメンではフロントエンド含め、エンジニアを募集しています。 興味のある方は是非ご覧ください!