今回は、実際に実務で取り入れたエラーハンドリングについて紹介していきます。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よりも上位にある必要があります。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。僕のアカウントはこちらです。
それではまた~