一、React Native 错误类型解析

React Native 应用的错误主要分为两大类:JavaScript 层错误与原生层错误,二者在触发场景、表现形式及处理方式上存在显著差异。

(一)JavaScript 层错误

这类错误发生在 React Native 的 JS 运行时(如 Hermes 或 JSC),多由代码逻辑缺陷导致,常见场景包括:变量未定义、函数调用方式错误、数组越界、异步操作异常等。

关键特征
  • 开发环境下:会触发 RedBox(红色错误提示框),显示错误信息、文件路径及堆栈跟踪,直接阻断应用运行。
  • 生产环境下:默认不会显示错误提示,若未处理会导致应用白屏、功能失效,严重时引发 JS 线程阻塞。
  • 可通过 React 生态工具或 JS 原生 API 捕获。
常见示例
// 1. 变量未定义错误
function greet() {
  console.log(name); // name 未声明,触发 ReferenceError
}

// 2. 异步操作错误(未捕获的 Promise 拒绝)
const fetchData = async () => {
  const response = await fetch('https://api.example.com/data');
  const data = await response.json();
  return data.user.name; // 若 data.user 为 undefined,触发 TypeError
};
fetchData(); // 未添加 catch 处理,导致未捕获 Promise 错误

(二)原生层错误

这类错误发生在 iOS 或 Android 的原生代码中,常见于自定义原生模块、第三方原生库兼容性问题、原生 API 调用不当等场景,例如:iOS 中数组越界、Android 中空指针异常、原生模块向 JS 传递非法数据等。

关键特征
  • 开发 / 生产环境下:通常直接导致应用 崩溃,并在原生日志(Xcode 控制台、Android Logcat)中输出堆栈跟踪。
  • 难以通过 JS 层直接捕获,需借助原生错误处理机制或跨层通信工具。
  • 影响范围更广,可能破坏应用进程稳定性,甚至导致用户无法重启应用。
常见示例
  1. iOS 原生错误(Swift)
// 自定义原生模块中数组越界
@objc func getRandomItem(_ callback: RCTResponseSenderBlock) {
  let items = ["a", "b"]
  let randomIndex = 3 // 超出数组长度(0-1)
  let item = items[randomIndex] // 触发 IndexOutOfRangeException,导致应用崩溃
  callback([nil, item])
}
  1. Android 原生错误(Kotlin)
// 原生模块中空指针异常
@ReactMethod
fun showToast(message: String) {
  val toast = Toast.makeText(null, message, Toast.LENGTH_SHORT) // context 为 null,触发 NullPointerException
  toast.show()
}

img

层级结构:应用层 → JavaScript 层(RedBox / 白屏)、原生层(iOS/Android 崩溃)→ 底层运行时(Hermes/JSC、原生系统 API)

二、核心错误处理工具与 API

针对不同类型的错误,React Native 提供了多层次的处理工具 —— 从 React 内置的错误边界,到 JS 运行时 API,再到原生层的崩溃捕获机制。

(一)JavaScript 层核心处理工具

1. Error Boundaries(错误边界)

Error Boundaries 是 React 16+ 引入的官方错误捕获机制,专门用于捕获子组件树中的 JS 错误(包括渲染错误、生命周期方法错误),并展示降级 UI,避免整个组件树崩溃。注意:它无法捕获异步操作(如 setTimeout、Promise)、事件处理器中的错误及服务器端渲染错误。

实现方式

需创建一个类组件,实现 getDerivedStateFromError(更新错误状态)和 componentDidCatch(日志上报)两个生命周期方法:

import React, { Component } from 'react';

class ErrorBoundary extends Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false, error: null };
  }

  // 静态方法:捕获错误并更新组件状态,用于渲染降级 UI
  static getDerivedStateFromError(error) {
    return { hasError: true, error };
  }

  // 实例方法:捕获错误信息,可用于日志上报
  componentDidCatch(error, errorInfo) {
    // 上报错误到监控平台(如 Sentry)
    console.error('Error Boundary 捕获错误:', error, errorInfo.componentStack);
  }

  render() {
    if (this.state.hasError) {
      // 降级 UI:向用户展示友好提示
      return (
        <div style={{ padding: 20, textAlign: 'center' }}>
          <h2>页面加载出错了</h2>
          <p>{this.state.error?.message}</p>
          <button onClick={() => this.setState({ hasError: false })}>
            刷新页面
          </button>
        </div>
      );
    }
    // 无错误时,渲染子组件树
    return this.props.children;
  }
}

// 使用方式:包裹可能出错的组件
export default function App() {
  return (
    <ErrorBoundary>
      <MainComponent /> {/* 可能触发 JS 错误的核心组件 */}
    </ErrorBoundary>
  );
}
2. React Native Error Utils

ErrorUtils 是 React Native 内置的 JS 错误捕获工具,可全局监听未被错误边界捕获的 JS 错误(包括异步操作错误),相当于 JS 层的 “最后一道防线”。

使用方式
import { ErrorUtils } from 'react-native';

// 保存原始错误处理函数(可选,便于后续恢复默认行为)
const originalErrorHandler = ErrorUtils.getGlobalHandler();

// 自定义全局错误处理函数
const customErrorHandler = (error, isFatal) => {
  // isFatal:布尔值,标识错误是否致命(可能导致应用崩溃)
  console.error(`全局捕获 JS 错误(${isFatal ? '致命' : '非致命'}):`, error);
  
  // 上报错误信息(如错误消息、堆栈跟踪、设备信息)
  reportErrorToMonitor({
    message: error.message,
    stack: error.stack,
    isFatal,
    platform: Platform.OS,
  });

  // 若需要保留默认行为(如开发环境显示 RedBox),可调用原始处理函数
  originalErrorHandler(error, isFatal);
};

// 注册全局错误处理函数
ErrorUtils.setGlobalHandler(customErrorHandler);
3. Promise 错误捕获

React Native 中未捕获的 Promise 拒绝(如未添加 catch 的异步请求)会触发警告(开发环境)或静默失败(生产环境),需通过以下方式统一处理:

// 监听未捕获的 Promise 拒绝
if (YellowBox) {
  // 开发环境:屏蔽特定警告(可选)
  YellowBox.ignoreWarnings(['Possible Unhandled Promise Rejection']);
}

// 全局捕获未处理的 Promise 错误
process.on('unhandledRejection', (reason, promise) => {
  console.error('未处理的 Promise 错误:', reason, promise);
  // 上报错误信息
  reportErrorToMonitor({
    type: 'UnhandledPromiseRejection',
    message: reason?.message || String(reason),
    stack: reason?.stack,
  });
});

(二)原生层错误处理工具

原生层错误(崩溃)无法通过 JS 工具直接捕获,需分别在 iOS 和 Android 端实现原生错误处理逻辑,或使用第三方监控库简化流程。

1. iOS 原生错误捕获(Swift/Objective-C)

iOS 中可通过 NSSetUncaughtExceptionHandler 捕获未处理的异常,通过 signal 监听信号量错误(如内存访问错误):

// AppDelegate.swift
import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
  var window: UIWindow?

  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // 注册异常捕获处理器
    NSSetUncaughtExceptionHandler { exception in
      let name = exception.name.rawValue
      let reason = exception.reason ?? "未知原因"
      let stackTrace = exception.callStackSymbols.joined(separator: "\n")
      
      // 保存错误日志到本地或上报
      let errorLog = "iOS 崩溃:\n名称:\(name)\n原因:\(reason)\n堆栈:\(stackTrace)"
      print(errorLog)
      // 调用自定义上报方法
      ErrorReporter.shared.report(errorLog: errorLog)
    }

    // 监听信号量错误(如 SIGSEGV、SIGABRT)
    let signals = [SIGABRT, SIGILL, SIGSEGV, SIGFPE, SIGBUS, SIGPIPE]
    for signal in signals {
      signal(signal) { sig in
        let errorLog = "iOS 信号量错误:信号 \(sig)"
        print(errorLog)
        ErrorReporter.shared.report(errorLog: errorLog)
        // 退出应用(避免僵尸进程)
        exit(sig)
      }
    }

    return true
  }
}
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐