Error Boundaryは、子コンポーネントツリー内のJavaScriptエラーをキャッチし、クラッシュページの代わりにフォールバックインターフェースを表示するReactコンポーネントです。React Nativeのコンテキストでは、致命的でないレンダリングエラーが発生した際にアプリケーションの完全なクラッシュを防ぎます。React Documentation, 2024によると、error boundaryは子コンポーネントのrenderメソッド、ライフサイクルフック、コンストラクタ内のエラーをキャッチし、アプリケーションが動作を継続できるようにします。React Nativeでは、モバイルアプリはF5で再読み込みできないため、これは特に重要です — ユーザーはセッション全体を失います。
重要なポイント
Error Boundaryは、レンダリングエラーが発生した際のグレースフルデグラデーションのためのReactメカニズムです。React 16(2017年)で、2つのライフサイクルメソッド(static getDerivedStateFromErrorまたはcomponentDidCatch)のいずれかを実装するラッパーコンポーネントとして導入されました。Error Boundaryにより、空の白い画面やアプリケーションの完全なクラッシュの代わりに、ユーザーに意味のあるメッセージを表示できます。
React 16以前は、render内の未処理のエラーはDOMツリー全体のアンマウントを伴う完全なアプリケーションクラッシュを引き起こしていました。Webアプリケーションでは白い空画面を意味し、React Nativeではホーム画面に戻る完全なクラッシュを意味しました。Reactチームは、宣言的UIのためのcatchブロックの類似物としてError Boundaryを導入し、Erlang言語の “let it crash” アプローチから概念を借用しました。
React Nativeでは、Error Boundaryがないと、レンダリングエラーが発生した際にアプリケーションが完全にクラッシュします。ユーザーは回復オプションなしに現在のセッション全体を失います。React NativeにおけるError Boundaryは非常に重要です。モバイルアプリはWebページのように再読み込みできないため、ユーザーセッションは不可逆的に失われ、ユーザーは最初からやり直さなければなりません。
Error BoundaryはReactツリーレベルで動作します。子コンポーネントがrenderまたはライフサイクル内でエラーをスローすると、Reactはツリー全体をアンマウントせず、階層内の最も近いError Boundaryに制御を渡します。BoundaryはgetDerivedStateFromErrorを呼び出し、state.hasError = trueに設定し、壊れたコンポーネントブランチの代わりにフォールバックUIをレンダリングします。
import React, { Component, ErrorInfo, ReactNode } from "react"
interface Props {
children: ReactNode
fallback?: ReactNode
}
interface State {
hasError: boolean
error?: Error
}
class ErrorBoundary extends Component<Props, State> {
constructor(props: Props) {
super(props)
this.state = { hasError: false }
}
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error }
}
componentDidCatch(error: Error, info: ErrorInfo) {
console.error("Caught by boundary:", error)
Crashlytics.recordException(error)
}
render() {
if (this.state.hasError) {
return this.props.fallback || <FallbackUI />
}
return this.props.children
}
}
getDerivedStateFromErrorはフォールバックUIをレンダリングするための状態を設定します — これは変更をコミットする前のレンダーフェーズ中に呼び出される静的メソッドです。componentDidCatchはコミットフェーズで実行され、副作用(ロギング、Crashlyticsへのクラッシュレポート送信、分析)を目的としています。2つのメソッドは、UI状態管理と副作用の間で責任を分離します。
Error Boundaryを作成するには、getDerivedStateFromErrorおよび/またはcomponentDidCatchメソッドを持つクラスコンポーネントを実装する必要があります。関数コンポーネントはError Boundaryになれません — Reactはライフサイクルメソッドへのアクセスが必要なため、この機能をクラスコンポーネントでのみサポートしています。react-error-boundaryライブラリは、利便性のためにフックベースのAPIを備えた既成の実装を提供します。
// React NativeでのError Boundaryの使用例
import { ErrorBoundary } from "react-error-boundary"
const FallbackComponent = ({ error, resetError }: FallbackProps) => (
<View style={styles.container}>
<Text>問題が発生しました</Text>
<Text>{error.message}</Text>
<Button title="再試行" onPress={resetError} />
</View>
)
const App = () => (
<SafeAreaView>
<ErrorBoundary FallbackComponent={<FallbackComponent />}>
<UserProfile userId={"123"} />
</ErrorBoundary>
<BottomNavigation />
</SafeAreaView>
)
ラッパーレベル — Error Boundaryは階層のさまざまなレベルに配置できます。アプリケーションルートに1つのグローバルBoundaryを置くと、エラー時にフォールバックUIが表示されますが、ナビゲーションは維持されます。画面レベルで複数のBoundaryを使用すると、エラーを分離できます。1つの画面が壊れても、残りは独立して機能し続けます。react-error-boundaryはuseErrorBoundaryフックを介してリセットメカニズムを簡素化し、再読み込みなしで状態をリセットできます。一般的なプロジェクトでは、React Nativeアプリケーションに3レベルのBoundaryスキームが最適と考えられています。
エラー発生後、ユーザーはhasErrorをfalseにリセットして子ツリーを再レンダリングする “再試行” ボタンをクリックできます。リセットメカニズムは、再読み込みなしでアプリケーション機能を復元するために重要です。react-error-boundaryでは、onResetコールバックが使用され、キャッシュのクリア、データの再取得、ツリーの上位での状態更新が可能です。
Error Boundaryは非同期エラーをキャッチしません — setTimeout、setInterval、Promise、async/await内のエラーです。Reactはレンダーサイクルとライフサイクルフックの外で発生するエラーをインターセプトできません。これらは異なる実行コンテキストで実行されるためです。非同期エラーに対しては、ハンドラ内に個別のtry-catchを配置するか、グローバルなunhandledrejectionイベントハンドラが必要です。
onClick、onChange、その他のイベントハンドラ内のエラーは、Reactレンダリングの外部で実行されるため、Error Boundaryによってキャッチされません。イベントハンドラのエラー処理は、try-catchを使用してハンドラ自体の中で行う必要があります。react-error-boundaryライブラリは、イベントハンドラから最も近いBoundaryにエラーをスローするためのuseErrorHandlerフックを提供します。
Error BoundaryはNext.jsやGatsbyのサーバーサイドでは機能しません。getDerivedStateFromErrorメソッドとcomponentDidCatchメソッドはSSR中に呼び出されません。ライフサイクルメソッドはブラウザでのみ利用可能だからです。サーバーエラーには、個別の戦略が必要です:getServerSidePropsでのtry-catch、error.jsフォールバックページ(Next.js 13+)、またはグローバルミドルウェア。
React Nativeでは、Error Boundaryはネイティブモジュールレベルのクラッシュを防ぎません。ネイティブクラッシュ(segfault、メモリ不足、ネイティブ例外)はObjective-CまたはJavaレベルで発生し、JavaScriptレイヤーには到達しません。ネイティブクラッシュには、Crashlytics NDK(Android)またはKSCrash(iOS)が必要です。Error BoundaryはReact NativeアプリケーションのJavaScriptレイヤーのみを保護します。
Error Boundaryは論理モジュールの境界に配置します:画面ごとに1つのBoundary、サードパーティウィジェットごとに1つ、複雑なフォームごとに1つです。これによりエラーが分離され、ユーザーはアプリケーションの他の部分で作業を続行できます。ルートBoundaryは常に存在する必要があります — 共通のナビゲーションコンポーネントやプロバイダでの重大なエラーに備えて。各Boundaryは自身のインターフェースフラグメントに責任を持ち、エラー発生時に隣接するコンポーネントに影響を与えません。
常にcomponentDidCatchを介してCrashlyticsまたはSentryにエラーを渡します。コンテキスト(画面名、userId、アプリバージョン、ナビゲーションパラメータ)を追加します。Sentryでは、クラッシュに至るまでのユーザーアクションのシーケンスであるbreadcrumbsが利用可能です。非致命的エラーの頻度を分析するには、イシューごとにグループ化されたCrashlyticsダッシュボードを使用します。
ベアなフォールバックは使用せず、意味のあるインターフェースを作成します。React Nativeの推奨セット:エラーメッセージ(ユーザーフレンドリーで技術的でないもの)、“再試行”ボタン、サポートまたはチャットへのリンク。空のViewは避けてください — ユーザーはアプリが完全にクラッシュしたと思い、閉じてしまいます。フォールバックはアプリケーション全体のデザインに統合される必要があります。
各Error BoundaryをReact Testing LibraryまたはReact Native Testing Libraryを使用してテストします。レンダリング時にエラーをスローするトリガーコンポーネントを作成し、フォールバックUIが表示されることを確認します。統合テストでは、異なるError Boundary状態(通常、エラー、リセット後)でstorybookを使用します。Boundaryの自動テストにより、コンポーネントが変更された場合でも、フォールバックUIが本番環境で正しく機能し続けることが保証されます。各Boundaryのテストカバレッジは、React Nativeプロジェクトのコードレビューの必須要件であるべきです。
よくある質問
ReactはError Boundaryをクラスコンポーネントでのみ実装します。componentDidCatchとgetDerivedStateFromErrorのライフサイクルメソッドへのアクセスが必要だからです。関数コンポーネントにはそのようなメソッドがありません。react-error-boundaryライブラリは、使いやすさのためにフックベースのAPIを備えた既成のクラスラッパーを提供します。
影響は最小限です — Error Boundaryは子ツリーのレンダリングごとに状態チェックを追加します。state.hasErrorの比較は定数時間のO(1)操作です。エラーがない場合、オーバーヘッドはありません。エラーが発生した場合にのみ、BoundaryがフォールバックUIの追加レンダリングを実行します。
いいえ、2–3レベルで十分です:アプリケーション全体のルートBoundary、各ナビゲーションブランチの画面Boundary、重要なウィジェット(支払いフォーム、マップ、チャット)のローカルBoundary。過剰なBoundaryの使用は、大きな利益なしにアーキテクチャを複雑にします。
Error BoundaryとSuspenseは独立しています:Suspenseはローディング(React 18+での保留中のPromise)をキャッチし、Error Boundaryはレンダリングエラーをキャッチします。これらは組み合わせることができます:<ErrorBoundary><Suspense><Component /></Suspense></ErrorBoundary>。ローディング中はSuspenseが最初にトリガーされ、ロードされたコンポーネントのエラー時にはError Boundaryがトリガーされます。
try-catchは同期命令コード内のエラーをキャッチしますが、JSXレンダリングエラーをインターセプトできません。Error Boundaryは宣言的UI向けに特別に設計されています:render、ライフサイクルフック、子コンポーネントのコンストラクタ内のエラーをインターセプトします。これはReactレンダリングの特性上、try-catchにはできないことです。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。