ReactのErrorBoundaryで内部のエラーをキャッチする
Error Boundary とは、自身の子コンポーネントツリーで発生した JavaScript のエラーをキャッチ・記録しフォールバックの UI を表示するコンポーネントです。
例えるなら、try/catch
構文を行うコンポーネントのようなものと言えます。
- イベントハンドラ
- 非同期コード(例:setTimeout や requestAnimationFrame のコールバック)
- サーバーサイドレンダリング
-(子コンポーネントではなく)error boundary 自身がスローしたエラー
エラーがキャッチされない場合の動作
ErrorBoundary の詳細を見る前に、エラーがキャッチされない場合の動作を確認しておきましょう。
React16 からコンポーネントツリー全体がアンマウントされます。
下記の簡単なコードを使って確認してみます。
const Red = () => {
return (
<div style={{backgroundColor: "red", flex: 1, height: 100 }}>Red Area</div>
)
}
const Green = () => {
return (
<div style={{backgroundColor: "green", flex: 1 ,height: 100 }}>Green Area</div>
)
}
const Blue = () => {
return (
<div style={{backgroundColor: "blue", flex: 1 ,height: 100}}>Blue Area</div>
)
}
function App() {
return (
<div style={{ display: "flex" }}>
<Red />
<Green />
<Blue />
</div>
)
}
export default App;
Red コンポーネント内で恣意的にエラーを発生させます。
const BadComponent = () => { throw new Error("something went wrong") }
const Red = () => {
return (
<div style={{backgroundColor: "red", flex: 1, height: 100 }}>
Red Area
<BadComponent />
</div>
)
}
画面が真っ白になってしまいました。
Error Boundaryコンポーネントの作成
真っ白な画面が表示されてしまうのはユーザーにとってあまりにも不便ですので、Error Boundary コンポーネントを作成してエラーが発生した際の UI を用意しましょう。
以下いずれかののライフサイクルメソッドを実装したクラスコンポーネントは Error Boundary コンポーネントとして扱われます。
公式の Error Boundary コンポーネントの例をそのまま掲載します。
export default 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;
}
}
static getDerivedStateFromError
このライフサイクルはフォールバック UI を描画するために使用されます。
getDerivedStateFromError()
は子孫コンポーネントによってエラーがスローされた際に呼び出されます。パラメーターとしてスローされたエラー受け取り、state を更新するための値を返す必要があります。
componentDidCatch
このライフサイクルは主にロギングなどの処理に使用されます。
componentDidCatch
は同じく子孫コンポーネントによってエラーがスローされた際に呼び出されます。
パラメーターとしてスローされたエラーとスタックトレース情報を受け取ります。
render
render
メソッドでは、getDerivedStateFromError
によってエラーをキャッチした状態であるならば、フォールバック UI を描画するようにします。
それ以外の場合には、子要素をそのまま描画します。
Error Boundaryコンポーネントの配置
トップレベルに配置
作成した Error Boundary コンポーネントを配置します。
まずはトップレベルに配置してみましょう。その場合には、すべての子孫コンポーネントのエラーをキャッチしてアプリケーション全体にフォールバックの UI が描画されます。
index.js
を編集します。
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import ErrorBoundary from './components/ErrorBoundary'
ReactDOM.render(
<ErrorBoundary>
<App />
</ErrorBoundary>,
document.getElementById('root')
);
個別のコンポーネントに配置
Error Boundary コンポーネントは、個別のコンポーネントずつラップして配置できます。
その場合、エラーが発生したコンポーネント以外の箇所はフォールバックの UI を表示せずにそのままの状態を維持することになります。
const Red = () => {
return (
<ErrorBoundary>
<div style={{backgroundColor: "red", flex: 1, height: 100 }}>
Red Area
<BadComponent />
</div>
</ErrorBoundary>
)
}
const Green = () => {
return (
<ErrorBoundary>
<div style={{backgroundColor: "green", flex: 1 ,height: 100 }}>Green Area</div>
</ErrorBoundary>
)
}
const Blue = () => {
return (
<ErrorBoundary>
<div style={{backgroundColor: "blue", flex: 1 ,height: 100}}>Blue Area</div>
</ErrorBoundary>
)
}
今度は Red コンポーネントのみにフォールバック UI が描画されました。
Discussion