mcpField
W
web-error-handling-error-boundaries

web-error-handling-error-boundaries

github.com/agents-inc開発2672 回インストール2 日前に更新
リポジトリ

web-error-handling-error-boundaries について

React のレンダーエラーをエラーバウンダリーで隔離し、フォールバック UI と復旧手段を設ける開発スキルです。クラス境界、react-error-boundary、非同期エラーの通知、React 19 のルートレベル報告を扱います。

主な機能

  1. 01クラスベースのエラーバウンダリー
  2. 02フォールバック UI とリセット操作
  3. 03showBoundary() による非同期エラー通知
  4. 04粒度を分けた境界の配置
  5. 05React 19 のルートエラー報告

活用シーン

  1. 01ウィジェットの障害によるページ全体の置き換えを防ぐ
  2. 02レンダーエラー後の再試行操作を追加する
  3. 03非同期障害をフォールバック UI に伝える
  4. 04React 19 のルートエラー報告を一元化する

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/agents-inc/skills --skill web-error-handling-error-boundaries

インストール後、Claude Code で/web-error-handling-error-boundariesを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

metadata.yaml
reference.md
examples/
core.md
react-19-hooks.md
recovery.md
testing.md

詳細

React エラーバウンダリ

クイックガイド: バウンダリは、レンダー中、ライフサイクルメソッド内、コンストラクター内で発生したエラーを捕捉し、サブツリーをフォールバック UI に置き換えます。イベントハンドラー、非同期処理、サーバーでのレンダー中に発生したエラーは捕捉しません。これらを捕捉するのは、何かが showBoundary() を呼び出した場合に限られます。getDerivedStateFromError と componentDidCatch に相当するフックがないため、バウンダリはクラスコンポーネントです。React 19 では createRoot に onCaughtError、onUncaughtError、onRecoverableError が追加されました。これらはレンダーではなくログを記録し、React 18 では無視されます。

詳細なリソース:

  • examples/core.md — クラスバウンダリ、react-error-boundary の使用方法、resetKeys、useErrorBoundary、きめ細かな配置
  • examples/react-19-hooks.md — createRoot/hydrateRoot のエラーオプション、captureOwnerStack()、エラーのフィルタリング
  • examples/recovery.md — 再試行の上限、指数バックオフ、エラーの分類
  • examples/testing.md — バウンダリをテスト可能にする要素と、それを実現するフィクスチャ
  • reference.md — バウンダリが捕捉するエラー、ライフサイクルとプロパティの表、チェックリスト

該当する方法

  • バウンダリの依存関係を追加したくない場合 — クラスを自分で実装します。API は getDerivedStateFromError と componentDidCatch のみです。完全な実装は examples/core.md を参照してください。
  • react-error-boundary が利用できる場合 — 再実装するのではなく、resetKeys、useErrorBoundary、FallbackProps 型を使い、 examples/core.md に従ってください。
  • React 19 を使っていて、UI ではなくログ記録が目的の場合 — 3 つの createRoot オプションは、バウンダリで捕捉されなかったエラーも含め、 すべてのエラーを報告します。examples/react-19-hooks.md を参照してください。

<critical_requirements>

エラーバウンダリーのコードを書く前に

getDerivedStateFromError から新しい状態を返し、副作用はすべて componentDidCatch に置いてください。 前者はレンダー中に実行されます。この段階でフェッチやログ出力を行うと、React のフェーズルールに違反します。後者はコミット時に実行されるため、安全です。

ルートだけでなく、機能領域ごとに専用のバウンダリーを設けてください。 ルートに単一のバウンダリーを置くと、ウィジェットひとつの障害でページ全体が空白になります。また、フォールバックが何の障害かを伝えられるのは、障害が起きた箇所の近くにある場合だけです。

フォールバックには復帰手段を用意してください。リセットコールバック、resetKeys、またはその両方を使います。 これがないと、ユーザーにできる復旧方法はページ全体の再読み込みだけになり、入力した内容がすべて失われます。

フォールバックには role="alert" を付け、操作要素には実際のボタンを使ってください。 そうしないと、サブツリーが消えても何も通知されず、スクリーンリーダーのユーザーは問題が起きたことを知る手段がありません。

非同期処理やイベントハンドラーのエラーは showBoundary() 経由で渡してください。 バウンダリーは拒否された Promise を検知できないため、未処理のままにすると、エラー状態がまったく表示されず、古いコンテンツが UI に残ります。

</critical_requirements>


自動検出: error boundary、ErrorBoundary、getDerivedStateFromError、componentDidCatch、fallback UI、react-error-boundary、useErrorBoundary、showBoundary、error fallback、onCaughtError、onUncaughtError、onRecoverableError、captureOwnerStack、FallbackProps、resetKeys

対象範囲:

  • レンダーフェーズのエラーを捕捉し、その箇所にフォールバック UI を表示すること
  • 捕捉したエラー後のリセットと再試行(再試行回数の上限やバックオフを含む)
  • バウンダリーの配置場所と、それぞれの適用範囲をどの程度にするかの判断
  • React ルートからのエラー報告の一元化

別途扱う内容:

  • サーバーレンダリング中にスローされたエラー — レンダリングフレームワークが、レンダリングに失敗した場合にクライアントへ何を送るかを決めます。クライアント側のバウンダリーはまだマウントされていません。
  • データレイヤーでのリクエスト失敗 — バウンダリーが捕捉できるのは、何かがエラーを再スローするか、showBoundary() を呼び出した場合だけです。再試行やキャッシュの無効化は、フェッチを行う側が担当します。
  • フィールド単位の検証フィードバック — 無効なフォームフィールドは想定される入力状態のため、サブツリーを置き換えるのではなく、インラインで表示します。
  • 監視先 — onError とルートのハンドラーは、エラーとコンポーネントスタックを渡します。それらをどこへ送るかは、報告ツールの役割です。

基本方針

どのバウンダリーにも捕捉されないレンダーエラーが起きると、ツリー全体がアンマウントされます。React は画面上に不完全なドキュメントを残すのではなく、ルートを破棄するため、どこであってもエラーがひとつスローされればページ全体が空白になります。

バウンダリーを設けることで、その影響範囲を隔離できます。影響範囲は最も近いバウンダリーが囲むサブツリーです。そのため、ひとつのバグでページのどの部分まで失われるかは、バウンダリーの配置で決まります。配置こそが本質的な判断であり、復旧方法、フォールバックの文言、ログ記録はすべてそこから導かれます。

バウンダリーは try/catch の代わりにはなりません。カバーするのは、try/catch では届かない唯一の領域、つまり React 自身のレンダーです。


基本パターン

パターン 1:クラスベースのバウンダリー

2 つのライフサイクルメソッドはフェーズごとに役割が分かれています。getDerivedStateFromError は純粋な処理として状態を返し、エラー報告は componentDidCatch で行います。

static getDerivedStateFromError(error: Error): State {
  return { hasError: true, error };
}

componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
  this.props.onError?.(error, errorInfo);
}

コード全文:examples/core.md

パターン 2:react-error-boundary

FallbackComponent には error と resetErrorBoundary が渡されるため、再試行の操作はデザインに応じた場所に配置できます。onError を使うと、エラー報告をフォールバックから切り離せます。

<ErrorBoundary FallbackComponent={ErrorFallback} onError={report}>
  <Dashboard />
</ErrorBoundary>;

function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
  return (
    <div role="alert">
      <p>{error.message}</p>
      <button onClick={resetErrorBoundary}>Try again</button>
    </div>
  );
}

プロパティの表は reference.md に、コード全文は examples/core.md にあります。

パターン 3:非同期エラーに対する showBoundary()

非同期処理でスローされたエラーは、それだけではバウンダリーに到達しません。useErrorBoundary を使うと、エラーを渡すための関数を取得できます。

const { showBoundary } = useErrorBoundary();

const load = async () => {
  try {
    const response = await fetch(endpoint);
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
  } catch (error) {
    showBoundary(error);
  }
};

障害時にサブツリーを置き換えたい箇所で使用してください。フィールド単位の検証メッセージのように、エラーを独自のインライン UI で表示する場合は使用しないでください。

コード全文:examples/core.md

### パターン 4:自動リセットのための `resetKeys`

リストされた値のいずれかが変更されると、エラーがクリアされて子コンポーネントが再レンダリングされます。これは、ナビゲーションやレコードの切り替えで必要な動作です。

<ErrorBoundary FallbackComponent={ErrorFallback} resetKeys={[currentPath]}>
  <PageContent path={currentPath} />
</ErrorBoundary>

比較は浅いため、オブジェクトや配列のキーには安定した参照が必要です。そうしないと、境界はレンダリングのたびにリセットされます。

パターン 5:適切な粒度での配置

各ウィジェットに個別の境界とフォールバックテキストを設定し、内側の境界で捕捉できなかったエラーはルート境界で捕捉します。

<ErrorBoundary fallback={<p>Chart unavailable</p>} onError={report}>
  <ChartWidget />
</ErrorBoundary>
<ErrorBoundary fallback={<p>Table unavailable</p>} onError={report}>
  <DataTable />
</ErrorBoundary>

3 つすべてのウィジェットを 1 つの境界で囲むと、最初のエラーで残り 2 つも巻き添えになります。

ただし、上限があります。単位は、ユーザーが単独で失敗したと認識する機能領域にします。すべてのコンポーネントを境界で囲むと、クラスコンポーネントや誰も読まないフォールバックテキストが増え、1 つのエラーが、率直なメッセージ 1 つではなく、小さな壊れたパネルが並ぶページに分割されてしまいます。

完全なコード:examples/core.md

パターン 6:フォールバック UI

フォールバックは、ユーザーが最悪の状況で目にする UI です。状況を伝え、そこから抜け出す方法を提供し、スタックトレースは開発用に保持します。

<div role="alert">
  <h2>Something went wrong</h2>
  {isDevelopment && <pre>{error.message}</pre>}
  <button onClick={resetErrorBoundary}>Try again</button>
</div>

<span onClick> による再試行はキーボードでは操作できず、本番環境で生のスタックトレースを表示すると内部情報が漏えいします。

完全なコード:examples/core.md

パターン 7:React 19 のルートエラーオプション

createRoot は、エラーを描画せずに報告する 3 つのハンドラーを受け取ります。エラーバウンダリーで捕捉できなかったエラーも対象です。これらはエラーバウンダリーを補完するものであり、置き換えるものではありません。

const root = createRoot(container, {
  onCaughtError: (error, info) => report("caught", error, info.componentStack),
  onUncaughtError: (error, info) =>
    report("uncaught", error, info.componentStack),
  onRecoverableError: (error, info) =>
    report("recoverable", error, info.componentStack),
});

完全なコード:examples/react-19-hooks.md


<red_flags>

注意すべき点

実行時に問題が発生するもの:

  • getDerivedStateFromError 内の副作用 — レンダリング中に実行されるため、そこでの fetch や setState は React のフェーズ規則に違反します。代わりに componentDidCatch から報告します。
  • エラーバウンダリー自身を囲むこと — エラーバウンダリーは自身のレンダリングエラーを捕捉できないため、エラーは親のエラーバウンダリーまたはルートまで伝播します。フォールバックはシンプルに保ちます。
  • 不安定な resetKeys の要素 — 浅い比較では、レンダリングのたびに新しい配列やオブジェクトだと判断され、エラーバウンダリーが継続的にリセットされます。値をメモ化するか、プリミティブ値をキーにします。
  • 再試行のコントロールとしての <span onClick={reset}> — フォーカスできず、Enter や Space でも操作できません。<button> を使います。

予想外の動作:

  • 非同期処理やイベントハンドラー内で発生したエラーはエラーバウンダリーに届かず、showBoundary() がなければ消えてしまいます。
  • 最も内側のエラーバウンダリーが優先されるため、広い範囲を対象とするルートのフォールバックが表示されるのは、内側のエラーバウンダリーがすべて捕捉できなかった場合だけです。
  • SSR のハイドレーションエラーは捕捉されたエラーではなく回復可能なエラーとして通知されるため、クライアント側のエラーバウンダリーでは捕捉できない場合があります。
  • 開発時のホットリロードによって、本番環境では発生しないエラーがエラーバウンダリーを作動させることがあります。
  • onCaughtError は、エラーバウンダリー自身の componentDidCatch の後に実行されます。
  • onRecoverableError の error.cause には、元のエラーが含まれていることがよくあります。
  • 開発環境以外では、captureOwnerStack() は null を返します。
  • 3 つのルートオプションは React 18 では何も通知せずに無視されます。警告もエラーもログもありません。

</red_flags>

コメント