エラーの発生箇所と処理方法
エラーの発生箇所は大きく以下のように分類できます。
- Reactコンポーネント- JSX
- Promise
- Promise以外(同期処理やsetTimeoutに渡すコールバック関数など)
 
- イベントハンドラ- Promise
- Promise以外(同期処理やsetTimeoutに渡すコールバック関数など)
 
- Native Modules
未処理のエラーを処理する方法
未処理のエラーは、Firebase Crashlytics SDKがエラーを捕捉してFirebase Crashlyticsにクラッシュログを送信します。
ただし、Promiseで発生したエラーに関しては、Firebase Crashlytics SDKで捕捉できないため、必ず個別にエラー処理を実施してください。
JavaScriptエンジンがHermesの場合、iOSではReactコンポーネントで発生したエラーがFirebase Crashlyticsに送信されない事象を確認しています。
回避策として、React.ComponentのcomponentDidCatchでエラーを再度throwすることで、Firebase Crashlyticsにクラッシュログが送信されます。
詳細は、FirebaseCrashlyticsWorkaround.tsxを参照してください。
Firebase Crashlyticsにログを送信するタイミングは、アプリを再起動した時になります。即座にログが送信されないことに注意してください。
Firebase Crashlyticsの設定
Firebase Crashlyticsの動作設定は、設定ファイルのFirebase.jsonで以下のように記述することで設定できます。
{
  "react-native": {
    "crashlytics_debug_enabled": false,
    "crashlytics_auto_collection_enabled": true,
    "crashlytics_ndk_enabled": true,
    "crashlytics_is_error_generation_on_js_crash_enabled": false,
    "crashlytics_javascript_exception_handler_chaining_enabled": true
  }
}
各パラメーターの定義と効果は、Crashlytics - Installation and getting started with Crashlytics を参照ください。
個別にエラーを処理する方法
Reactコンポーネントのエラー処理
Reactコンポーネントでは、JSXやコンポーネントのライフサイクルに応じた処理(componentDidMountやuseEffectなど)でエラーが発生します。
JSXでエラーが発生した場合は、Error Boundaryでエラーを捕捉します。該当のコンポーネントをError Boundaryでラップしてエラー処理を実施してください。
const Avator = () => {
  return (
    // アバター表示に失敗した場合は、デフォルトのアバターにフォールバックする
    <AvatorErrorBoundary>
      <ExternalAvator />
    </AvatorErrorBoundary>
  );
};
JSXのエラーは基本的にプログラムの不具合によるものです。ほとんどのユースケースにおいて、Error Boundaryを使用して個別にエラー処理を実施することはないと思われます。
Error Boundaryは、イベントハンドラで発生したエラーを捕捉できません。JSX内のイベントハンドラで発生したエラーに関しては、イベントハンドラのエラー処理を参照してください。
JSX以外で発生したエラーをハンドリングする場合は、発生元がPromiseかどうかで対応が変わります。
Promiseの場合は、以下のいずれかの方法でエラーを捕捉します。
- Promise.catch()を使用してエラーを捕捉します。
- await式を利用した場合は- try...catch文で囲んでエラーを捕捉します。
const Component = () => {
  useEffect(() => {
    asyncFunction
      .then((value) => {
        // 正常時の処理
      })
      .catch((e) => {
        // エラー処理
      });
  }, [asyncFunction]);
  /* ~省略~ */
};
const Component = () => {
  const callAsyncFunction = useCallback(async () => {
    try {
      const result = await asyncFunction();
      // 正常時の処理
    } catch (e) {
      // エラー処理
    }
  }, [asyncFunction]);
  useEffect(() => {
    callAsyncFunction().catch(() => {
      // callAsyncFunctionでエラーハンドリングを実施しているので、ここでは特に何もしない
    });
  }, [callAsyncFunction]);
  /* ~省略~ */
};
Promise以外の場合は、try...catch文で囲んでエラーを捕捉します。setTimeoutなどに渡すコールバック関数では、エラーを関数内で捕捉する必要があることに注意してください。
const Component = () => {
  useEffect(() => {
    try {
      const result = syncFunction();
      // 正常時の処理
    } catch(e) {
      // エラー処理
    }
    setTimeout(() => {
      // コールバック関数内でエラーを捕捉する
      try {
        const result = syncFunction();
        // 正常時の処理
      } catch(e) {
        // エラー処理
      }
    });
  }, [syncFunction]);
  /* ~省略~ */
};
コンポーネントのライフサイクルに応じた同期処理で発生したエラーも、Error Boundaryで捕捉できます。しかし、それらのエラーもError Boundaryで捕捉してしまうと、個々のエラーに応じた処理を実施することが難しくなります。
そのため、JSX以外の同期処理で発生したエラーについてはError Boundaryを使用しないで、個別にエラー処理を実施します。
イベントハンドラのエラー処理
イベントハンドラで発生するエラーを個別にハンドリングする場合は、Reactコンポーネントで説明している内容と同様です。Promise.catch()、またはtry...catchを利用してエラーを捕捉してください。
const Component = () => {
  const syncEventHandler = useCallback(() => {
    try {
      const result = syncFunction();
      // 正常時の処理
    } catch(e) {
      // エラー処理
    }
    setTimeout(() => {
      // コールバック関数内でエラーを捕捉する
      try {
        const result = syncFunction();
        // 正常時の処理
      } catch(e) {
        // エラー処理
      }
    });
  }, [syncFunction]);
  return (
    <View>
      <Button onPress={syncEventHandler} />
    </View>
  );
};
const Component = () => {
  const asyncEventHandler = useCallback(() => {
    asyncFunction
      .then((value) => {
        // 正常時の処理
      })
      .catch((e) => {
        // エラー処理
      });
  }, [asyncFunction]);
  return (
    <View>
      <Button onPress={asyncEventHandler} />
    </View>
  );
};
Native Modulesのエラー処理
Native ModulesはJava(Kotlin)やObjective-C(Swift)、C++などのネイティブ言語を使用したモジュールです。OSが提供しているAPIなどを利用する時に作成します。
Native Modulesで発生するエラーを個別にハンドリングする場合は、言語毎のエラー処理方式に従ってエラーを捕捉してください。
また、Native Modulesで発生したエラーをJavaScript側に伝えたい場面もあります。その場合は、JavaScript側からエラー時に実行したいコールバック関数を受け取るか、Native ModulesからPromiseを返却する方法があります。詳細は、React Nativeのドキュメントを参照してください。