ReactのErrorBoundaryとは?
ErrorBoundaryは、子コンポーネントで何らかのエラーが発生した時にエラー画面をフォールバックUIとして表示してくれるコンポーネントです。
2022年8月時点でも、ErrorBoundaryはクラスコンポーネントとして定義する必要があり、現在主流である関数型コンポーネントとして作成することができません。
ErrorBoundaryコンポーネントはクラスコンポーネントに、ライフサイクルメソッドである static getDerivedStateFromError()
か componentDidCatch()
を定義すると、作成する事ができます。
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 <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
また、イベントハンドラでのエラーや、非同期処理のエラー等、以下は処理することができません。
- イベントハンドラ
- setTimeoutなどに代表される非同期コード
- サーバサイドレンダリング
- ErrorBoundary自身がスローしたエラー
これらを関数型コンポーネントでうまく扱える方法がないかなぁと探していたところ、react-error-boundaryというパッケージがあり、クラスコンポーネントでは扱えないエラーも簡単に処理できそうだったので、試してみました。
react-error-boundaryを試してみる
react-error-boundaryは、レンダー時のエラーを扱いやすくしてくれるラッパーです。
インストールや基本的な使い方は公式ドキュメントを参照してみて下さい。
エラー発生時に表示するコンポーネントを追加します。
渡ってきたpropsからエラー情報を取得して表示します。
import React from "react";
export const ErrorFallback = ({ error, resetErrorBoundary }) => {
return (
<div>
<h1>エラーが発生しました</h1>
<p>{error.message}</p>
<button onClick={resetErrorBoundary}>Try again</button>
</div>
);
};
続いて検証のために、一定の確率でエラーを起こすコンポーネントを作成します。
import React from "react";
export const SamplePage = () => {
const random = Math.random();
if (random <= 0.5) {
throw new Error("Something went wrong");
}
return (
<div>
<h2>SamplePage</h2>
</div>
);
};
最後にフォールバックUIを表示させたいコンポーネントを、react-error-boundaryからimportしたErrorBoundaryでWrapします。
import React from "react";
import ReactDOM from "react-dom/client";
import { ErrorBoundary } from "react-error-boundary";
import App from "./App";
import { ErrorFallback } from "./components/ErrorBoundary";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<ErrorBoundary FallbackComponent={ErrorFallback}>
<App />
</ErrorBoundary>
);
これでエラー発生時に代わりのUIを表示する事が出来ました。
gif画像にある「Try againのボタンには、propsのresetErrorBoundary
に指定されているコールバックを実行することで、少し時間を置いてからリカバリーするという選択肢もユーザーに提供する事ができます。(リカバリーする状況であれば)
最後に
ドキュメントを読むと、イベントハンドラや非同期処理のエラーまで扱えるようになるので、クラスコンポーネントのErrorBoundaryで扱えないものも考慮したい場合、手軽に扱えそうなので、良い選択肢だと思います。