エラーハンドリング:axios+react-error-boundary

◆ Live配信スケジュール ◆
サイオステクノロジーでは、Microsoft MVPの武井による「わかりみの深いシリーズ」など、定期的なLive配信を行っています。
⇒ 詳細スケジュールはこちらから
⇒ 見逃してしまった方はYoutubeチャンネルをご覧ください
【5/21開催】Azure OpenAI ServiceによるRAG実装ガイドを公開しました
生成AIを活用したユースケースで最も一番熱いと言われているRAGの実装ガイドを公開しました。そのガイドの紹介をおこなうイベントです!!
https://tech-lab.connpass.com/event/315703/

今回は、実際に実務で取り入れたエラーハンドリングについて紹介していきます。Axiosのinterseptorからreact-error-boundaryにエラーを通知・処理をする仕組みを作成しています。実務の悩みエラーハンドリング設計第一弾

初めに

どもども、平社員の龍ちゃんです。4月からもブログ投稿を頑張っていきたいですね。今回は、実務で適応しているエラーハンドリングについて一つ形がまとまったので共有していきたいと思います。今回使用している技術は以下になります。今回作成した環境はこちらになります。

  • axios Interseptor
  • react-error-boundary
  • Suspense

こちらの機能を使用して非同期通信のエラーハンドリングを構築していきます。概要としては、「Axios Interseptorを用いてエラーをキャッチした場合、react-error-boundaryに通知して表示を切り替える」となります。

それでは実際の設計から実装まで進めていきたいと思います。僕自身も悩んでいる内容なので、心意気のあるフロントエンド担当者の方はDM待ってます。

構成

今回、非同期処理のエラーハンドリングを実装するために使用するライブラリと機能は以下になります。それぞれブログで紹介しています。

基本コンセプトとしては、「Axios Interseptorを用いてresponseを監視して、エラーが発生した場合は【react-error-boundary】のuseErrorHandlerを使用して通知」になります。

バージョンが4に上がってから、react-error-boundary伝搬の書き方が変わりました!!ここはバージョン3までの書き方です!。バージョンアップによってhooksが追加されました。説明部分を以下に変更してください。

const { showBoundary } = useErrorBoundary();
showBoundary(error);

コンセプトを図にした内容を以下に示します。実際にデータを取得する際には、Suspenseを利用してデータ取得を処理します。Suspense を用いることで【react-error-boundary】でエラーをキャッチすることができるようになります。公式でもSuspenseを用いてデータをキャッチする方法としてerror-boudnaryを使用するサンプルが紹介されています。

Axios Interseptorとreact-error-boundary

コンポーネント設計は以下になります。

コンポーネント設計

コンポーネントの順番は重要です。Axios Interseptorから通知を行うために【react-error-boundary】がAxios Interseptorよりも上位にある必要があります。Suspense を使用すれば、GETのタイミングでエラーが発生するとキャッチすることができますが、【react-error-boundary】ですべて処理するようにAxios Interseptorで明示的にエラーを伝搬させます。

コーディング

コーディングの解説に入っていこうと思います。コーディングの分割としては、コンポーネント設計の通りに解説します。

  • react-error-boundary:エラーを表示するためのコンポーネント
  • Axios Interseptor:エラーをキャッチして伝搬させるためのコンポーネント
  • 実際の使用方法用サンプル

react-error-boundary エラー表示用コンポーネント

こちらでは、エラーを受け取りエラーを表示するコンポーネントを作成します。使用方法としては、適当なファイルに適当な名前を付けて以下のコードをコピー&ペーストをしましょう。

import { ErrorBoundary, FallbackProps } from 'react-error-boundary';

import { AxiosError } from 'axios';

type Props = {
  children: React.ReactNode;
};

export const ReactErrorBoundaryComponent = (props: Props) => {
  const { children } = props;
  return <ErrorBoundary FallbackComponent={ErrorFallback}>{children}</ErrorBoundary>;
};

const ErrorFallback = ({ error, resetErrorBoundary }: FallbackProps) => {
  // axios error
  if (error instanceof AxiosError)
    return (
      <>
        <pre>
          react-error-boundary axios error {error.message} | {error.code}
        </pre>
        <button type="button" onClick={resetErrorBoundary}>
          reset button
        </button>
      </>
    );

  //   normal error
  return (
    <>
      <pre>react-error-boundary {error.message}</pre>
      <button type="button" onClick={resetErrorBoundary}>
        reset button
      </button>
    </>
  );
};

こちらでは、【react-error-boundary】によるエラー表示を設定しています。ErrorFallbackでは、エラーを受け取ります。errorがAxiosのエラーかどうかを判定して、表示するコンポーネントを切り替えます。本来であればステータスコードによって表示を切り替える必要があります。

Axios Interseptor エラーキャッチ用コンポーネント

こちらでは、Axios Interseptorによってエラーを【react-error-boundary】のuseErrorHandlerを使用して伝搬させます。使用方法としては、適当なファイルに適当な名前を付けて以下のコードをコピー&ペーストをしましょう。


import { useEffect } from 'react';
import { useErrorHandler } from 'react-error-boundary';

import axios, { AxiosError, AxiosResponse } from 'axios';

export const axiosClient = axios.create({
  timeout: 1000,
  headers: {
    'Content-Type': 'application/json',
  },
  baseURL: 'http://localhost:4242/dummy/',
});

type Props = {
  children: React.ReactNode;
};

export const AxiosErrorHandlingComponent = (props: Props) => {
  const { children } = props;
  const errorHandler = useErrorHandler();

  useEffect(() => {
    const responseInterceptor = axiosClient.interceptors.response.use(
      (response: AxiosResponse) => {
        return response;
      },
      (error: AxiosError) => {
        errorHandler(error);
        return Promise.reject(error);
      }
    );

    // クリーンアップ
    return () => {
      axiosClient.interceptors.response.eject(responseInterceptor);
    };
  }, [errorHandler]);

  return <>{children}</>;
};

Axios Interseptorの使用方法については、別途ブログを書いているのでそちらで説明を読んでいただく方が良いかと思います。

実際の使用方法サンプル

こちらでは、先ほど作成したファイルを使用してコンポーネント設計に合わせて構築していきます。コンポーネント名としては以下になります。

  • 【react-error-boundary】:ReactErrorBoundaryComponent
  • 【Axios Interseptor】:AxiosErrorHandlingComponent
import { Suspense } from 'react';

import { AxiosGetCompoennt } from './components/pages/AxiosTestComponent';
import { AxiosErrorHandlingComponent } from './utilities/AxiosClientComponent';
import { LoadingComponent } from './utilities/LoadingComponent';
import { ReactErrorBoundaryComponent } from './utilities/ReactErrorBoundaryComponent';

function App() {
  return (
    <>
      <ReactErrorBoundaryComponent>
        <AxiosErrorHandlingComponent>
          <Suspense fallback={<LoadingComponent />}>
            <main className="flex h-full w-full flex-col items-center justify-center">
              <AxiosGetCompoennt />
            </main>
          </Suspense>
        </AxiosErrorHandlingComponent>
      </ReactErrorBoundaryComponent>
    </>
  );
}

export default App;

コンポーネントの構成の順番としては、一番上の層にReactErrorBoundaryComponentを次にAxiosErrorHandlingComponentを構築します。次に好みによりますが、Suspense をデータアクセスするコンポーネントより上位の位置に設置します。今回であればAxiosGetComponentでデータアクセスします。

AxiosGetComponentは以下の内容です。


import { axiosClient } from '@/utilities/AxiosClientComponent';
const flag = { dataFetch: false };

export const AxiosGetCompoennt = () => {
  const data = dataFetch();
  return <>{data}</>;
};
const dataFetch = () => {
  if (!flag.dataFetch) throw axiosClient.get('400').then(() => (flag.dataFetch = true));
  return 'data get';
};
 

Suspenseを機能させるためには、Promiseをthrowする必要があります。Promiseが解決されるまで、fallbackに設定しているコンポーネントが表示されます。内容としては、400エラーが出る画面にアクセスしてエラーを表示しています。

終わりに

今回は、実務で採用しようと考えているエラーハンドリング方法について解説しました。データの取得部分に関して言えばサンプルのため簡易的な部分になりますが、エラーハンドリングの骨組みに関しては以下の方法で進めていこうと思います。

僕自身かなり悩んでいる内容になります。構築方法に関しては、ベストではないが無難な選択かなと思っています。一般的かどうかは先輩がいないので判断がつきません。なので、先輩フロントエンドエンジニアもしくは一緒に悩んでくれるエンジニアの皆さんはTwitterでお問い合わせお願いします。応援メッセージも待ってますねw。僕のアカウントはこちらです。

それではまた~

アバター画像
About 龍:Ryu 107 Articles
2022年入社で主にフロントエンドの業務でTailwindと遊ぶ日々。お酒とうまいご飯が好きで、運動がちょっと嫌いなエンジニアです。しゃべれるエンジニアを目指しておしゃべりとブログ執筆に注力中(業務もね)//
ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

0人がこの投稿は役に立ったと言っています。


ご覧いただきありがとうございます。
ブログの最新情報はSNSでも発信しております。
ぜひTwitterのフォロー&Facebookページにいいねをお願い致します!



>> 雑誌等の執筆依頼を受付しております。
   ご希望の方はお気軽にお問い合わせください!

Be the first to comment

Leave a Reply

Your email address will not be published.


*


質問はこちら 閉じる