🧐

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

4 min read

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

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

ErrorBoundaryは次のエラーをキャッチすることができないことに中止してください。

  • イベントハンドラ
  • 非同期コード(例: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

ログインするとコメントできます