Error Boundaryの必要性
Reactアプリケーションでは、コンポーネント内の1つのキャッチされていないJavaScriptエラーがUI全体をクラッシュさせる可能性があります。Error Boundaryは、子コンポーネントツリー内で発生したJavaScriptエラーをキャッチし、クラッシュする代わりにフォールバックUIを表示するReactコンポーネントです。
Error Boundaryの実装
Error Boundaryはクラスコンポーネントで、getDerivedStateFromError と componentDidCatch のライフサイクルメソッドを実装します:
import React, { Component, ErrorInfo, ReactNode } from "react";
interface State {
hasError: boolean;
error: Error | null;
}
class ErrorBoundary extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error };
}
componentDidCatch(error: Error, info: ErrorInfo) {
logErrorToService(error, info);
}
render() {
if (this.state.hasError) {
return this.props.fallback || <DefaultFallback />;
}
return this.props.children;
}
}
フォールバックUIの設計
適切なフォールバックUIは回復オプションを提供し、ユーザーのフラストレーションを軽減します。リフレッシュボタンやエラーメッセージの表示、開発環境では詳細なスタックトレースを表示する構成が推奨されます。
Sentryとの統合
Error BoundaryはSentryなどのエラーログサービスと自然に統合できます:
componentDidCatch(error: Error, info: ErrorInfo) {
Sentry.withScope((scope) => {
scope.setExtras({ componentStack: info.componentStack });
Sentry.captureException(error);
});
}
Sentryは組み込みのError Boundaryラッパーも提供しており、簡単に統合できます。
バウンダリの配置戦略
戦略的なError Boundaryの配置が重要です。各主要セクション(サイドバー、メインコンテンツ、モーダル)を独立してラップし、ルートバウンダリは最終手段として使用します。障害が回復可能な機能境界にバウンダリを配置し、健全な部分は機能し続けられるようにします。
ガイドライン:
- サイドバー、メインコンテンツ、モーダルは個別にラップ
- ルートバウンダリは最終手段としてのみ使用
- 回復可能な機能境界に配置
- 正常な部分は機能し続けられるようにする
エラーリカバリパターン
フォールバック表示に加えて、Error Boundaryは回復メカニズムを提供できます。状態をリセットして再レンダリングを試みる「Retry」パターンや、エラー発生時に特定のコンポーネントをアンマウントして再マウントする手法が効果的です。
Error Boundaryの制限
Error Boundaryには重要な制限があります:
| 制限 | 説明 |
|---|---|
| イベントハンドラ | onClick等のエラーはキャッチ不可 — try-catchを使用 |
| 非同期コード | setTimeoutやPromiseのエラーはキャッチ不可 |
| SSR | サーバーサイドのエラーはキャッチ不可 |
| 自身のエラー | 自身のエラーはキャッチ不可 |
| グローバル状態 | 破損した状態の回復には非対応 |
まとめ
Error Boundaryは本番環境のReactアプリケーションに不可欠な要素です。UIの完全なクラッシュを防ぎ、グレースフルな劣化を提供し、ログサービスとシームレスに統合できます。戦略的な配置と適切なログ記録、リカバリパターンを組み合わせることで、障害に強く、開発者に実用的な診断情報を提供するアプリケーションを構築できます。
