🧐

ReactのErrorBoundaryで内部のエラーをキャッチする

2021/06/20に公開

Error Boundary とは、自身の子コンポーネントツリーで発生した JavaScript のエラーをキャッチ・記録しフォールバックの UI を表示するコンポーネントです。

例えるなら、try/catch 構文を行うコンポーネントのようなものと言えます。

  • イベントハンドラ
  • 非同期コード(例:setTimeout や requestAnimationFrame のコールバック)
  • サーバーサイドレンダリング
    -(子コンポーネントではなく)error boundary 自身がスローしたエラー

エラーがキャッチされない場合の動作

ErrorBoundary の詳細を見る前に、エラーがキャッチされない場合の動作を確認しておきましょう。

React16 からコンポーネントツリー全体がアンマウントされます。

下記の簡単なコードを使って確認してみます。

App.js
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 コンポーネント内で恣意的にエラーを発生させます。

App.js
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 コンポーネントの例をそのまま掲載します。

App.js
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 を編集します。

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 を表示せずにそのままの状態を維持することになります。

App.js
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 が描画されました。

GitHubで編集を提案

Discussion