Error Boundary
多くのフロントエンド開発者は、エラー処理とログに関する記録を見落としがちです。
コードがエラーをスローした場合、正しい処理をする必要があります。
Reactでのエラーはコンポーネント内で適切な処理をしなければいけません。
コンポーネント内で発生したJavaScriptエラーは Reactの内部まで侵されます。
つまり、エラー境界の外側でエラーが発生すると Reactコンポーネントツリー全体がアンマウントされアプリケーションそのものがクラッシュされます。
React v16からは、開発時に発生したエラーはコンポーネントのスタックトレースとして、コンポーネントツリーがどこでエラーが発生してるのかは、ファイル名や行番号まで表示し正確に分かるようになってます。
これは、Create-React-Appでの開発の際はデフォルトで最初から有効になっております。
しかしCRAを使用しない場合、その機能は開発用であり、本番環境では必ず無効化
にしなければいけない決まりがあります。
Reactのエラーを管理、ログとして記録する方法があります。
そちらを学びましょう。
エラー境界とは?
Reactは、JavaScriptエラーをキャッチして効率的に処理するために、エラー境界を追加しました。
エラー境界は、コンポーネントのUI部分で発生する JavaScriptのエラーをキャッチして処理する方法として、React v16で導入されました。
つまり、子コンポーネントツリーの任意の場所で JavaScriptエラーをキャッチし、それらのエラーをログに記録し、クラッシュしたコンポーネントツリーの代わりにフォールバックUIを表示するReactコンポーネントという事です。
したがって、エラー境界はライフサイクルメソッド、レンダリングメソッド、およびuseEffect
のようなフック内で発生するエラーのみをキャッチします。
エラー境界の考え方は、アプリケーションの任意の部分を特別なコンポーネント(いわゆるエラー境界)でラップできるということです。
アプリケーションのその部分でキャッチされていないエラーが発生した場合、そのコンポーネント内に含まれます。
その後にエラーを表示し、エラー報告サービスに報告して、可能であれば回復を試みることができます。
Reactのドキュメントによると、エラー境界は次のエラーを処理する事はありません。
・ イベントハンドラー
・ 非同期コード
・ サーバー側レンダリング
・ エラー境界自体でスローしたエラー
上記のように、基本的にエラー境界はReactが関与するコード部分のエラーのみを処理します。
アプリケーション全体で複数のエラー境界を作成できますが、多くのアプリケーションはルートレベルで1つのエラー境界を選択する傾向があることに注意するようにしてください。
そして、エラー境界はクラスコンポーネントでのみ使用できます。
React-Error-Boundary
エラー境界はクラスコンポーネントでのみ使用できるので、関数コンポーネントでは機能しません。
react-error-boundary
は、Reactのエラー境界のラッパーであり、開発者がエラー境界をゼロから構築することなくコードに実装できるようにします。
react-error-boundaryを使用すると、提供されたコンポーネントでエラーが予想される<ErrorBoundary>
コンポーネントを単純にラップし、追加のProps
を渡してエラー境界の動作をカスタマイズ可能です。
react-error-boundaryパッケージを使用すれば、React フックにエラー境界を実装できます。
// index.js import React from 'react' import ReactDOM from 'react-dom' import ErrorBoundary from 'react-error-boundaries' const App = () => { return ( <ErrorBoundary> <YourComponents /> </ErrorBoundary> ); } ReactDOM.render(<App />, document.getElementById('root'));
クラスコンポーネント
まず、クラスコンポーネントでの例を見ていきましょう。
エラー境界を作成するには、クラスコンポーネントを作成し、エラー境界がエラーをキャッチしたかどうかを判断するための状態変数を定義するだけです。
クラスコンポーネントには、少なくとも3つのメソッドが必要となります。
・ getDerivedStateFromError
エラー境界の状態を更新するために使用されるgetDerivedStateFromError
という静的メソッドです。
・ componentDidCatch
エラー境界がエラーを検出したときに操作を実行するためのcomponentDidCatch
ライフサイクルメソッド です。(エラーログサービスへのログ記録など)
・ render
エラー発生時に、エラー境界の子やフォールバック UIをレンダリングするためのrender
メソッドです。
// クラスコンポーネント class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false }; } static getDerivedStateFromError(error) { //フォールバックUIが表示されるように状態の更新 return { hasError: true }; } componentDidCatch(error, errorInfo) { //エラーをエラー報告サービスに記録 logErrorToMyService(error, errorInfo); } render() { if (this.state.hasError) { //フォールバックUIをレンダリング return <h1>Something went wrong.</h1>; } return this.props.children; } }
使用する場合は、コンポーネント内にラップしてあげるだけです。
<ErrorBoundary> <MyComponent /> </ ErrorBoundary>
クラスコンポーネントを使用している時は、このcomponentDidCatch()
ライフサイクルメソッド機能でエラーログ情報を記録する事ができます。
それだけではなく、静的関数であるgetDerivedStateFromError()
を使用して状態を更新し、render
メソッド内でエラーが発生した場合にJavaScriptエラーをキャッチし記録してからフォールバックUIを表示します。
getDerivedStateFromError(error)
これらはエラー処理をする為の優れた最善の方法となります。
抑えておくべき事は、先述でも触れた通り次のエラーを処理する事はありません。
イベントハンドラー、非同期コールバック、サーバー側のレンダリングなどでエラー処理は不可能になります。
Try-Catchを使用したエラー処理
Try-Catch
ブロックに関しては、エラー境界によりキャッチされる事がないエラーを処理する為に使われたりします。
しかし子コンポーネントの例外をキャッチしません プログラムの処理を命令する時のコードのみで動作されます。
なので子コンポーネントの例外を処理する為の最善方法とは言えません。
関数コンポーネント
関数コンポーネントでエラー境界を作成するには、react-error-boundarというパッケージをインストールしなければいけません。
下記でインストールします。
npm install --save react-error-boundary
GitHub - bvaughn/react-error-boundary: Simple reusable React error boundary component
この、react-error-boundarではError Boundary
を関数コンポーネントでも使用できるラッパーを提供してくれます。
関数コンポーネントでのError-Boundary
はエラー処理を簡潔にしエラー処理の制限が解放されたものになります。
フォールバックを表示しエラー境界と同じようにログを記録しアプリケーションの状態をリセットし、エラーがまた起きないように再発防止をする事が可能になります。
// 関数コンポーネント import {ErrorBoundary} from 'react-error-boundary'; function MyFallbackComponent({error, resetErrorBoundary}) { return ( <div role="alert"> <p>Something went wrong:</p> <pre>{error.message}</pre> <button onClick={resetErrorBoundary}>Try again</button> </div> ) } export default function App() { return ( <ErrorBoundary FallbackComponent={MyFallbackComponent} onError={(error, errorInfo) => errorService.log({ error, errorInfo })} onReset={() => { // 状態のリセット }} > <MyErrorProneComponent /> </ErrorBoundary> ); }
react-error-boundaryを使用すると、React開発者は、記述する必要があるコードの量を削減し、エラー境界機能を拡張して、通常のエラー境界では識別されない他の形式のエラーをキャッチできるようになります。
そしてクラスとの違いとしては、カスタムフックを使用すればイベントハンドラーと非同期のコードエラー処理を可能にします。
アプリケーション内で複数の異なるエラー処理を記述して使用する事もありません。
最後に
ほとんどのフロントエンド開発者はコンソールログに関しては精通しているかと思います。
ですが、Reactでのエラーをログに記録していく方法を理解している開発者の方はごく少数です。
この記事を読み、自分自身の力でエラーの処理を適切に記録し高品質なアプリケーション開発に役立つ事を私は願っています。
そして素敵なコーディングライフを送って下さい。
本日は以上となります。
この記事が気に入ったら、ブックマークし他の方に共有してください👏
最後まで読んで頂きありがとうございます。