ReactのErrorBoundaryとは何か
ErrorBoundary
コンポーネントツリーのどこかで例外が発生した場合、アプリケーション全体が影響を受けてしまう。アプリの規模が大きくなると、例外が発生した箇所を特定するのが難しくなってくる。
ErrorBoundaryは、特定のコンポーネントで例外が発生したとしても、アプリ全体に影響を与えずフォールバック用の UI を表示するコンポーネント。
ErrorBoundaryコンポーネントの作成
ErrorBoundaryを実装するには、ライフサイクルメソッドの getDerivedStateFromError
か、 componentDidCatch
を使う必要がある。つまり、関数コンポーネントでのErrorBoundaryはできず、クラスコンポーネントを実装しないといけないみたい。
ErrorBoundary.tsx
import React, { ErrorInfo } from "react";
class ErrorBoundary extends React.Component<{}, { hasError: boolean }> {
constructor(props: {}) {
super(props);
this.state = {
hasError: false
};
}
static getDerivedStateFromError(): { hasError: boolean } {
console.log("getDerivedStatefromError");
return { hasError: true };
}
componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
console.log("this.state.hasError", this.state.hasError);
console.log(error);
console.log(errorInfo);
}
render() {
if (this.state.hasError) {
console.log("render", this.state.hasError);
return <>エラーが発生したよ!</>;
}
return <>{this.props.children}</>;
}
}
export default ErrorBoundary;
ほぼ公式ドキュメントからの抜粋 error boundaryとは
-
getDerivedStateFromError()
... 名前のとおり、エラーをキャッチしてstateを更新するためのオブジェクト。更新するstate情報をreturnすることでsetState()される。 -
componentDidCatch()
... エラー情報をキャッチする。ErrorとErrorInfo
該当コンポーネントをErrorBoundaryコンポーネントで囲む
エラーを発生させるコンポーネントを作成。
BreakThings.tsx
export const BreakThings = () => {
throw new Error("Something went wrong!");
};
App.tsx
import "./styles.css";
import SiteLayout from "./SiteLayout";
import ErrorBoundary from "./ErrorBoundary";
import { BreakThings } from "./BreakThings";
export default function App() {
return (
<div className="App">
<SiteLayout
menu={
<ErrorBoundary>
<BreakThings />
<p>Menu</p>
</ErrorBoundary>
}
>
<>
<h1>Heading</h1>
</>
</SiteLayout>
</div>
);
}
単一のコンポーネントをラップすると、それぞれでエラーが発生した場合、個別にfallbackコンポーネントが表示されます。
ただし、開発用サーバーだとスタックトレース情報が表示されてしまうので、アプリケーションをビルドした上で確認したほうが良さそう。
関数型で扱えるパッケージreact-error-boundary
react-error-boundaryを使用すれば、レンダリング時のエラーが扱いやすくなるみたい。
import {ErrorBoundary} from 'react-error-boundary'
function ErrorFallback({error, resetErrorBoundary}) {
return (
<div role="alert">
<p>Something went wrong:</p>
<pre>{error.message}</pre>
<button onClick={resetErrorBoundary}>Try again</button>
</div>
)
}
const ui = (
<ErrorBoundary
FallbackComponent={ErrorFallback}
onReset={() => {
// reset the state of your app so the error doesn't happen again
}}
>
<ComponentThatMayError />
</ErrorBoundary>
)****
ドキュメントからの抜粋ですが、やはり関数型の方がシンプルで良き。