iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >ReactNative错误采集原理在Android中如何实现
  • 295
分享到

ReactNative错误采集原理在Android中如何实现

2023-07-05 06:07:25 295人浏览 薄情痞子
摘要

今天小编给大家分享一下ReactNative错误采集原理在Android中如何实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下

今天小编给大家分享一下ReactNative错误采集原理在Android中如何实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

1 js错误

1.1 Error

Error是错误基类,其他错误继承自Error,Error对象有两个主要属性,name和message

new Error(message)

1.2 常见的错误

SyntaxError:语法错误

语法错误是一种常见的错误,在所有编程语言中都存在,表示不符合编程语言规范。

一类是词法、语法分析转换生成语法树时发生,此类异常一旦发生,导致整个js文件无法执行,而其他异常发生在代码运行时,在错误出现的那一行之前的代码不受影响

const 1xx; // SyntaxError

另一类是运行中出现的语法错误,如开发中常见的JSON解析错误,参数传入非标准json字符

JSON.parse('') // SyntaxError: Unexpected end of JSON input
ReferenceError:引用错误

引用了一个不能存在的变量,变量未声明就引用了

const a = xxx; // ReferenceError: xxx is not defined
TypeError:类型错误

变量或参数不是有效类型

1() // TypeError: 1 is not a functionconst a = new 111() // TypeError: 111 is not a constructor
RangeError:边界错误

超出有效范围时发生异常,常见的是数组长度超出范围

[].length = -1 // RangeError: Invalid array length
URIError:URI错误

调用URI相关函数中出现,包括encodeURI、decodeURI()、encodeURIComponent()、decodeURIComponent()、escape()和unescape()

decodeURI('%') // URIError: URI malfORMed

1.3 自定义错误

我们可以继承Error类,实现自定义的错误

class MyError extends Error {    constructor(message) {        super(message);        this.name = 'MyError';    }}function() {  throw new MyError('error message'); // MyError: error message}

2 RN错误处理

RN错误处理包括JS和native两部分,由JS捕获,抛给Native处理

2.1 JS部分

2.1.1 MessageQueue

Native和JS通信的消息队列, 负责Native和JS通讯, 包括渲染、交互、各种互相调用等。所有的通信都会经过_guard函数处理,在_guard中会被try-catch住,出现异常后调用ErrorUtils处理

__guard(fn: () => void) {    if (this.__shouldPauseOnThrow()) {      fn();    } else {      try {        fn();      } catch (error) {        ErrorUtils.reportFatalError(error); // 捕获异常,交给ErrorUtils      }    }  }

注:react-native/Libraries/BatchedBridge/MessageQueue.js

2.1.2 ErrorUtils

ErrorUtils用于处理RN中所有的异常,它对暴露异常处理拦截接口

  • 异常上报

收到异常后调用_globalHandler处理异常

// 处理非fatal异常reportError(error: mixed): void {    _globalHandler && _globalHandler(error, false);},// 处理fatal异常reportFatalError(error: mixed): void {    _globalHandler && _globalHandler(error, true); },
  • 异常处理

所有异常通过_globalHandle函数处理,默认情况下_globalHandler会直接将错误抛出,ErrorUtils对外提供了setGlobalHanlder做错误拦截处理,RN重写_globalHandler来做错误收集和处理

let _globalHandler: ErrorHandler = function onError(  e: mixed,  isFatal: boolean,) {  throw e;};setGlobalHandler(fun: ErrorHandler): void {    _globalHandler = fun;},getGlobalHandler(): ErrorHandler {    return _globalHandler;},

注:react-native/Libraries/polyfills/error-guard.js

2.1.3 ExceptionsManager

ExceptionsManager是RN中异常管理模块,负责红屏处理、console.error、并将异常传给Native侧

异常处理器设置

  • 调用ErrorUtils.setGlobalHandler,把错误处理实现交给ExceptionsManager.handleException

  • console.error处理:调用ExceptionsManager.installConsoleErrorReporter重写console.error

const ExceptionsManager = require('./ExceptionsManager');// Set up console.error handlerExceptionsManager.installConsoleErrorReporter();// Set up error handlerif (!global.__fbDisableExceptionsManager) {  const handleError = (e, isFatal) => {    try {      ExceptionsManager.handleException(e, isFatal);    } catch (ee) {      console.log('Failed to print error: ', ee.message);      throw e;    }  };  const ErrorUtils = require('../vendor/core/ErrorUtils');  ErrorUtils.setGlobalHandler(handleError);}

注:react-native/Libraries/Core/setUpErrorHandling.js

ExceptionsManager处理异常

  • 构建Error:如果错误不是Error类型,构造一个SyntheticError,方便日志输出和展示

function handleException(e: mixed, isFatal: boolean) {  let error: Error;  if (e instanceof Error) {    error = e;  } else {    error = new SyntheticError(e);  }  reportException(error, isFatal);}
  • 调用错误处理

function reportException(e: ExtendedError, isFatal: boolean) {  const NativeExceptionsManager = require('./NativeExceptionsManager').default;  if (NativeExceptionsManager) {    // 解析错误,获取错误信息、堆栈    const parseErrorStack = require('./Devtools/parseErrorStack');    const stack = parseErrorStack(e);    const currentExceptionID = ++exceptionID;    const originalMessage = e.message || '';    let message = originalMessage;    if (e.componentStack != null) {      message += `\n\nThis error is located at:${e.componentStack}`;    }    const namePrefix = e.name == null || e.name === '' ? '' : `${e.name}: `;    const isFromConsoleError = e.name === 'console.error';    if (!message.startsWith(namePrefix)) {      message = namePrefix + message;    }    // 如果是console.error则输出    if (!isFromConsoleError) {      if (console._errorOriginal) {        console._errorOriginal(message);      } else {        console.error(message);      }    }    message =      e.jsEngine == null ? message : `${message}, js engine: ${e.jsEngine}`;    // 抑制(不展示)红屏,不展示native红屏弹窗,forceRedbox默认为false    const isHandledByLogBox =      e.forceRedbox !== true && global.__unstable_isLogBoxEnabled === true;    const data = preprocessException({      message,      originalMessage: message === originalMessage ? null : originalMessage,      name: e.name == null || e.name === '' ? null : e.name,      componentStack:        typeof e.componentStack === 'string' ? e.componentStack : null,      stack,      id: currentExceptionID,      isFatal,      extraData: {        jsEngine: e.jsEngine,        rawStack: e.stack,        // Hack to hide native redboxes when in the LogBox experiment.        // This is intentionally untyped and stuffed here, because it is temporary.        suppressRedBox: isHandledByLogBox,      },    });    // 如果抑制native红屏,展示JS红屏提示错误    if (isHandledByLogBox) {      LogBoxData.addException({        ...data,        isComponentError: !!e.isComponentError,      });    }    // 把调用NativeExceptionsManager上报给native    NativeExceptionsManager.reportException(data);  }}
  • NativeExceptionsManager调用native模块上报错误

// Native导出类,以Android为例,对应ExceptionsManagerModule.javaconst NativeModule = TurboModuleReGIStry.getEnforcing<Spec>(  'ExceptionsManager',);const ExceptionsManager{  // 判断是否是fatal调用不同函数上报reportException(data: ExceptionData): void {    if (data.isFatal) {      ExceptionsManager.reportFatalException(data.message, data.stack, data.id);    } else {      ExceptionsManager.reportSoftException(data.message, data.stack, data.id);    }  },   // 上报fatal异常 reportFatalException(    message: string,    stack: Array<StackFrame>,    exceptionId: number,  ) {    NativeModule.reportFatalException(message, stack, exceptionId);  },  // 上报soft异常reportSoftException(    message: string,    stack: Array<StackFrame>,    exceptionId: number,  ) {    NativeModule.reportSoftException(message, stack, exceptionId);  },  // Android提供关闭红屏函数dismissRedbox(): void {    if (Platform.OS !== 'iOS' && NativeModule.dismissRedbox) {      // TODO(T53311281): This is a noop on iOS now. Implement it.      NativeModule.dismissRedbox();    }  },}

console.error处理

上述提到调用ExceptionsManager.installConsoleErrorReporter处理console.error,处理成非fatal异常

function installConsoleErrorReporter() {  // 如果设置过,return  if (console._errorOriginal) {    return; // already installed  }  console._errorOriginal = console.error.bind(console);  // 设置console.error处理函数  console.error = reactConsoleErrorHandler;  if (console.reportErrorsAsExceptions === undefined) {    console.reportErrorsAsExceptions = true;  }}// console.error处理函数,最终调用reportException上报成非fatal异常function reactConsoleErrorHandler() {  if (arguments[0] && arguments[0].stack) {    // 上报    reportException(arguments[0],  false);  } else {    // 构造一个SyntheticError    const stringifySafe = require('../Utilities/stringifySafe');    const str = Array.prototype.map      .call(arguments, value =>        typeof value === 'string' ? value : stringifySafe(value),      )      .join(' ');    const error: ExtendedError = new SyntheticError(str);    error.name = 'console.error';// 上报    reportException(error,  false);  }}

注:react-native/Libraries/Core/ExceptionsManager.js

注:跟进上述源码可知,红屏是通过isHandledByLogBox参数可以禁止native红屏弹窗,isHandledByLogBox是通过global.__unstable_isLogBoxEnabled控制,可以通过下面方式禁止native红屏展示,但是还是会展示js红屏来提示错误

global.__unstable_isLogBoxEnabled = true;YellowBox.__unstable_enableLogBox(); // 内部调用了上面的代码

2.2 Native部分

2.2.1 ExceptionsManagerModule

上面讲述了JS处理异常后将异常抛给native处理,ExceptionsManagerModule是native处理异常模块,导出给JS类名为ExceptionsManager

ExceptionsManagerModule异常处理

// 上报fatal异常@ReactMethodpublic void reportFatalException(String message, ReadableArray stack, int id) {    JavaOnlyMap data = new JavaOnlyMap();    data.putString("message", message);    data.putArray("stack", stack);    data.putInt("id", id);    data.putBoolean("isFatal", true);    reportException(data);}// 上报soft异常@ReactMethodpublic void reportSoftException(String message, ReadableArray stack, int id) {    JavaOnlyMap data = new JavaOnlyMap();    data.putString("message", message);    data.putArray("stack", stack);    data.putInt("id", id);    data.putBoolean("isFatal", false);    reportException(data);}// 最终调用reportException@ReactMethodpublic void reportException(ReadableMap data) {  // 错误堆栈    String message = data.hasKey("message") ? data.getString("message") : "";    ReadableArray stack = data.hasKey("stack") ? data.getArray("stack") : Arguments.createArray();    int id = data.hasKey("id") ? data.getInt("id") : -1;    boolean isFatal = data.hasKey("isFatal") ? data.getBoolean("isFatal") : false;  // dev模式,展示红屏dialog    if (mDevSupportManager.getDevSupportEnabled()) {      // 获取是否抑制红屏参数,对应js侧传入的isHandledByLogBox      boolean suppressRedBox = false;      if (data.getMap("extraData") != null && data.getMap("extraData").hasKey("suppressRedBox")) {        suppressRedBox = data.getMap("extraData").getBoolean("suppressRedBox");      }      if (!suppressRedBox) {        mDevSupportManager.showNewJSError(message, stack, id); // 显示红屏弹窗      }    } else {// fatal抛出javascriptException异常,非fatal打印出来      if (isFatal) {        throw new JavascriptException(jsStackTrace)          .setExtraDataAsJson(extraDataAsJson);      } else {        logException(jsStackTrace, extraDataAsJson);      }    }}@ReactMethodpublic void dismissRedbox() {    if (mDevSupportManager.getDevSupportEnabled()) {      mDevSupportManager.hideRedboxDialog();    }}
// 上报soft异常- (void)reportSoft: (NSString *)message stack:(NSArray<NSDictionary *> *)stack exceptionId:(double)exceptionId suppressRedBox: (BOOL) suppressRedBox {    if (!suppressRedBox) {        [_bridge.redBox showErrorMessage:message withStack:stack errorCookie:((int)exceptionId)];    }    if (_delegate) {      [_delegate handleSoftJSExceptionWithMessage:message stack:stack exceptionId:[NSNumber numberWithDouble:exceptionId]];    }}// 上报fatal异常- (void)reportFatal: (NSString *)message stack:(NSArray<NSDictionary *> *)stack exceptionId:(double)exceptionId suppressRedBox: (BOOL) suppressRedBox {    if (!suppressRedBox) {        [_bridge.redBox showErrorMessage:message withStack:stack errorCookie:((int)exceptionId)];    }    if (_delegate) {      [_delegate handleFatalJSExceptionWithMessage:message stack:stack exceptionId:[NSNumber numberWithDouble:exceptionId]];    }    static NSUInteger reloadRetries = 0;    if (!RCT_DEBUG && reloadRetries < _maxReloadAttempts) {      reloadRetries++;      RCTTriggerReloadCommandListeners(@"JS Crash Reload");    } else if (!RCT_DEV || !suppressRedBox) {      NSString *description = [@"Unhandled JS Exception: " stringByAppendingString:message];      NSDictionary *errorInfo = @{ NSLocalizedDescriptionKey: description, RCTJSStackTraceKey: stack };      RCTFatal([NSError errorWithDomain:RCTErrorDomain code:0 userInfo:errorInfo]);    }}// reportExceptionRCT_EXPORT_METHOD(reportException:(JS::NativeExceptionsManager::ExceptionData &)data){  NSString *message = data.message();  double exceptionId = data.id_();  id<NSObject> extraData = data.extraData();  // Reserialize data.stack() into an array of untyped dictionaries.  // TODO: (moti) T53588496 Replace `(NSArray<NSDictionary *> *)stack` in  // reportFatalException etc with a typed interface.  NSMutableArray<NSDictionary *> *stackArray = [NSMutableArray<NSDictionary *> new];  for (auto frame: data.stack()) {    NSMutableDictionary * frameDict = [NSMutableDictionary new];    if (frame.column().hasValue()) {      frameDict[@"column"] = @(frame.column().value());    }    frameDict[@"file"] = frame.file();    if (frame.lineNumber().hasValue()) {        frameDict[@"lineNumber"] = @(frame.lineNumber().value());    }    frameDict[@"methodName"] = frame.methodName();    if (frame.collapse().hasValue()) {        frameDict[@"collapse"] = @(frame.collapse().value());    }    [stackArray addObject:frameDict];  }  NSDictionary *dict = (NSDictionary *)extraData;  BOOL suppressRedBox = [[dict objectForKey:@"suppressRedBox"] boolValue];  if (data.isFatal()) {    [self reportFatal:message stack:stackArray exceptionId:exceptionId suppressRedBox:suppressRedBox];  } else {    [self reportSoft:message stack:stackArray exceptionId:exceptionId suppressRedBox:suppressRedBox];  }}

问题:fatal错误抛出异常后为什么应用为什么没有退出呢?

DevSupportManager处理红屏

 @Override  public void showNewJavaError(@Nullable String message, Throwable e) {    FLog.e(ReactConstants.TAG, "Exception in native call", e);    showNewError(        message, StackTraceHelper.convertJavaStackTrace(e), JAVA_ERROR_COOKIE, ErrorType.NATIVE);  }// 展示红屏弹窗private void showNewError(      @Nullable final String message,      final StackFrame[] stack,      final int errorCookie,      final ErrorType errorType) {    UiThreadUtil.runOnUiThread(        new Runnable() {          @Override          public void run() {            if (mRedBoxDialog == null) {              Activity context = mReactInstanceManagerHelper.getCurrentActivity();              mRedBoxDialog = new RedBoxDialog(context, DevSupportManagerImpl.this, mRedBoxHandler);            }            if (mRedBoxDialog.isshowing()) {              return;            }            Pair<String, StackFrame[]> errorInfo = processErrorCustomizers(Pair.create(message, stack));            mRedBoxDialog.setExceptionDetails(errorInfo.first, errorInfo.second);            mRedBoxDialog.resetReporting();            mRedBoxDialog.show();          }        });  }
2.2.2 线程异常捕获(Android)

Handle捕获异常

RN引擎创建的时候会初始化三个线程,UiThread、NativeModulesThread、JSThread,这些线程通过MessageQueueThreadHandler处理消息队列,MessageQueueThreadHandler重写了Handle的dispatchMessage函数,函数通过try-catch包裹防止应用直接退出,出现异常时调用QueueThreadExceptionHandler处理(引擎实现此接口),这里能拦截所有的异常,包括上述js捕获传到native手动抛出的、yoga布局过程中的等等

public class MessageQueueThreadHandler extends Handler {  private final QueueThreadExceptionHandler mExceptionHandler;  public MessageQueueThreadHandler(Looper looper, QueueThreadExceptionHandler exceptionHandler) {    super(looper);    mExceptionHandler = exceptionHandler;  }  @Override  public void dispatchMessage(Message msg) {    try {      super.dispatchMessage(msg);    } catch (Exception e) {      mExceptionHandler.handleException(e);    }  }}

引擎处理异常

在引擎(CatalystInstanceImpl)的内部类NativeExceptionHandler中,实现了QueueThreadExceptionHandler接口,在引擎创建时初始化,出现异常时调用NativeModuleCallExceptionHandler处理,并销毁引擎

// 内部类实现QueueThreadExceptionHandler,叫异常交给引擎的onNativeException处理private static class NativeExceptionHandler implements QueueThreadExceptionHandler {    @Override    public void handleException(Exception e) {      if (ReactFeatureFlags.enableCatalystCleanupFix) {        CatalystInstanceImpl catalystInstance = mCatalystInstanceImplWeak.get();        if (catalystInstance != null) {          catalystInstance.onNativeException(e);        }      } else {        mCatalystInstanceImpl.onNativeException(e);      }    }  }// 调用NativeModuleCallExceptionHandler处理异常,并销毁引擎private void onNativeException(Exception e) {    mHasNativeError.set(true);    boolean isAlive = !mDestroyed;    if (isAlive) {      mNativeModuleCallExceptionHandler.handleException(e);    }    mReactQueueConfiguration      .getUIQueueThread()      .runOnQueue(        new Runnable() {          @Override          public void run() {            // 销毁引擎            destroy(() -> {              if (mDestroyFinishedCallback != null) {                mDestroyFinishedCallback.onDestroyFinished();                mDestroyFinishedCallback = null;              }            });          }        });  }

注:com.facebook.react.bridge.CatalystInstanceImpl(引擎实现类)

2.2.3 最终的异常处理

默认处理方式

上述讲到引擎捕获异常后会调用NativeModuleCallExceptionHandler.handleException处理,它是个接口,引擎提供了默认实现类,默认实现类收到异常后是直接抛出,会导致应用退出

public interface NativeModuleCallExceptionHandler {    void handleException(Exception e);  void handleCaughtException(Exception e);}// 默认实现类public class DefaultNativeModuleCallExceptionHandler implements NativeModuleCallExceptionHandler {  @Override  public void handleException(Exception e) {    if (e instanceof RuntimeException) {      // Because we are rethrowing the original exception, the original stacktrace will be      // preserved.      throw (RuntimeException) e;    } else {      throw new RuntimeException(e);    }  }  @Override  public void handleCaughtException(Exception e) {    e.printStackTrace();  }}

自定义异常处理

为了防止默认处理方式将异常直接抛出导致crash,业务可以实现自定义的NativeModuleCallExceptionHandler接口来处理异常,将异常上报,并展示错误兜底页面

3 整体流程

基于上述源码解析可知,RN错误采集流程由JS侧中MessageQueue发起,经过一系列处理和封装,传到native侧,再经过native一系列转发,最终交给由引擎(CatalyInstanceImple)处理,整体流程如下图所示

ReactNative错误采集原理在Android中如何实现

4 错误兜底

页面出现异常后,对异常状态兜底是一种保障线上质量的常规手段。当页面发生严重 JS 错误(FatalError)时,会展示错误页面无法继续使用。这种方式在一些业务场景下并不友好。比如:页面上某一个次要模块发生异常,并不影响核心功能的使用,这种情况下展示出错页面有些不必要

React 16 中引入了一个新概念&mdash;&mdash;错误边界(Error Boundaries)。错误边界是一种 React 组件,这种组件可以捕获并打印发生在其子组件树任何位置的 JavaScript 错误,并且它会渲染出备用 UI,而不是渲染那些崩溃了的子组件树。错误边界能在渲染期间、生命周期方法和整个组件树的构造函数中捕获错误

基于这个特性,业务能够自定义控制接收到JSError的行为,能更优雅地处理错误兜底及展示

4.1 什么是错误边界

4.1.1 概念

错误边界是一种 React 组件,这种组件可以捕获并打印发生在其子组件树任何位置的 JS 错误,并且它会渲染出备用 UI,而不是渲染那些崩溃了的子组件树。错误边界能在渲染期间、生命周期方法和整个组件树的构造函数中捕获错误

4.1.2 错误边界的关键模块

错误边界是通过 try-catch 方式捕获异常的,它在哪里进行捕获异常的呢?React 有三个重要组成模块,错误边界在 Reconciliation 中对异常进行捕获。

React基础模块(这个模块定义了React的基础api及组件相关内容。对应我们开发页面时引入的 'react' 模块)

渲染模块(这个模块对于不同类型的应用,采用不同的渲染方式。对应我们开发页面时引入的 'react-dom' 模块)

Reconciliation 模块(又叫“协调模块”,这个模块是上面两个模块的基础,主要负责任务协调、生命周期函数管理等)

4.1.3 Reconciliation介绍

Reconciliation模块是React三个重要模块之一,又叫“协调模块”,这个模块是上面两个模块的基础,主要负责任务协调、生命周期函数管理等,它分为render和commit两个阶段

  • render阶段:简单来说就是找到需要更新的工作,通过 Diff Fiber Tree 找出要做的更新工作,这是一个js计算过程,计算结果可以被缓存,计算过程可以被打断,也可以恢复执行。

  • commit阶段:提交更新并调用对应渲染模块(react-dom)进行渲染,为了防止页面抖动,该过程是同步且不能被打断

// Reconciliation阶段开始,render阶段,performSyncWorkOnRoot(同步更新)、performConcurrentWorkOnRoot(异步)function performSyncWorkOnRoot(root) {      do {        try {          workLoopSync();          break;        } catch (thrownValue) {          handleError(root, thrownValue);        }      } while (true);}function handleError(root, thrownValue) {  do {    try {      throwException(        root,        workInProgress.return,        workInProgress,        thrownValue,        renderExpirationTime      );      workInProgress = completeUnitOfWork(workInProgress);    } catch (yetAnotherThrownValue)       thrownValue = yetAnotherThrownValue;      continue;    } // Return to the normal work loop.    return;  } while (true);}function throwException(  root,  returnFiber,  sourceFiber,  value,  renderExpirationTime) {     case ClassComponent:          var _update2 = createClassErrorUpdate(            workInProgress,            errorInfo,            renderExpirationTime          );          enqueueCapturedUpdate(workInProgress, _update2);          return;        }}function createClassErrorUpdate(fiber, errorInfo, expirationTime) {  var update = createUpdate(expirationTime, null);  update.tag = CaptureUpdate;  var getDerivedStateFromError = fiber.type.getDerivedStateFromError;  if (typeof getDerivedStateFromError === "function") {    var error = errorInfo.value;    update.payload = function() {      logError(fiber, errorInfo);      return getDerivedStateFromError(error);    };  }  var inst = fiber.statenode;  if (inst !== null && typeof inst.componentDidCatch === "function") {    update.callback = function callback() {      {        markFailedErrorBoundaryForHotReloading(fiber);      }      if (typeof getDerivedStateFromError !== "function") {        markLegacyErrorBoundaryAsFailed(this); // Only log here if componentDidCatch is the only error boundary method defined        logError(fiber, errorInfo);      }      var error = errorInfo.value;      var stack = errorInfo.stack;      this.componentDidCatch(error, {        componentStack: stack !== null ? stack : ""      });      {        if (typeof getDerivedStateFromError !== "function") {          !(fiber.expirationTime === Sync)            ? warningWithoutStack$1(                false,                "%s: Error boundaries should implement getDerivedStateFromError(). " +                  "In that method, return a state update to display an error message or fallback UI.",                getComponentName(fiber.type) || "Unknown"              )            : void 0;        }      }    };  } else {    update.callback = function() {      markFailedErrorBoundaryForHotReloading(fiber);    };  }  return update;}

注:源码react-native/Libraries/Renderer/ReactFabric-dev.js

错误边界不支持hooks组件,因为错误边界的实现借助了this.setState可以传递callback的特性,useState无法传入回调,所以无法完全对标

4.2 错误边界的使用

4.2.1 如何定义一个错误边界

前面提到错误边界捕获异常之后会交给特定的方法处理,如果一个组件重写了特定的方法,这个组件就是一个错误边界组件。

定义:如果一个类组件定义了生命周期方法中的任何一个(或两个)static getDerivedStateFromError() 或 componentDidCatch(),那么它就成了一个错误边界。 使用static getDerivedStateFromError()在抛出错误后渲染回退UI。 使用 componentDidCatch() 来记录错误信息。如下:

export class ErrorBoundary extends Component<IProps, IState> {    constructor(props) {        super(props);        this.state = {            hasError: false        };    }        static getDerivedStateFromError(_error) {        return {            hasError: true        };    }        componentDidCatch(error: Error) {        // 上报错误    }    render() {        if (this.state.hasError) {            return <Text style={style.errorDesc}>出错了</Text>;        }        return this.props.children;    }}
4.2.2 如何使用错误边界

将要捕获的组件用错误边界组件包裹

export default class Example extends PureComponent<Props, State> {    render() {        return <View style={ styles.container }>            <ErrorBoundary>                {                    this.renderErrorBlock()                }            </ErrorBoundary>            <Text style={ styles.other }>other block</Text>        </View>;    }    renderErrorBlock = () => {        return <View style={ styles.errorBoundary }>            '' && <Text style={ styles.error }>error block</Text>        </View>;    }}

4.3 适用范围

4.3.1 错误边界不能捕获哪些异常
  • 事件处理:点击事件

  • 异步代码:setTimeout 或 requestAnimationFrame 回调函数等

  • 错误边界自身抛出的错误

4.3.2 建议使用场景
  • 将影响整体页面展示逻辑的模块使用错误边界包裹并设置宽高,防止其他模块计算出错

  • 将非核心模块包裹,保障在非核心模块出错时核心模块展示不受影响

  • 包裹外部依赖的组件,防止意外的错误

  • 包裹独立展示模块,如广告,活动弹窗等

以上就是“ReactNative错误采集原理在Android中如何实现”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注编程网精选频道。

--结束END--

本文标题: ReactNative错误采集原理在Android中如何实现

本文链接: https://www.lsjlt.com/news/350322.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • ReactNative错误采集原理在Android中如何实现
    今天小编给大家分享一下ReactNative错误采集原理在Android中如何实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下...
    99+
    2023-07-05
  • ReactNative错误采集原理在Android中实现详解
    目录引言1 JS错误1.1 Error1.2 常见的错误SyntaxError:语法错误ReferenceError:引用错误TypeError:类型错误RangeError:边界错...
    99+
    2023-02-28
    Android ReactNative错误采集 Android ReactNative
  • SQL Server中如何实现错误处理
    今天小编给大家分享一下SQL Server中如何实现错误处理的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。一、SQ...
    99+
    2023-06-30
  • 如何在 Golang 中处理 HTTP 错误?
    非常抱歉,由于您没有提供文章标题,我无法为您生成一篇高质量的文章。请您提供文章标题,我将尽快为您生成一篇优质的文章。...
    99+
    2024-05-14
  • 如何在 Golang 中测试错误处理?
    非常抱歉,由于您没有提供文章标题,我无法为您生成一篇高质量的文章。请您提供文章标题,我将尽快为您生成一篇优质的文章。...
    99+
    2024-05-14
  • 如何在 Golang 中处理数据库错误?
    非常抱歉,由于您没有提供文章标题,我无法为您生成一篇高质量的文章。请您提供文章标题,我将尽快为您生成一篇优质的文章。...
    99+
    2024-05-14
  • 如何在java中使用HttpClient处理错误
    如何在java中使用HttpClient处理错误?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。Java有哪些集合类Java中的集合主要分为四类:1、List列表:有序的,可...
    99+
    2023-06-14
  • 如何通过 монаd 在Golang中处理错误?
    monad 是一种编程模式,可简化错误处理:monad 是一种具有 bind 和 return 操作的类型。bind 操作将值转换为包含在 monad 中的其他值。return 操作创建...
    99+
    2024-04-15
    错误处理 monaad golang
  • 如何在JavaScript中优雅地处理错误
    在JavaScript中,错误处理是一个非常重要的方面。一个健壮的应用程序应该能够处理各种各样的错误,并以一种优雅和信息丰富的方式向用户报告这些错误。 处理错误最常用的一种方法是使用try/catch语句。try/catch语句允许你指定...
    99+
    2024-02-27
  • 如何在 Golang 中优雅地处理错误?
    非常抱歉,由于您没有提供文章标题,我无法为您生成一篇高质量的文章。请您提供文章标题,我将尽快为您生成一篇优质的文章。...
    99+
    2024-05-14
  • 如何在 JavaScript 中捕获和处理错误
    ...
    99+
    2024-04-02
  • 如何在 PHP 中处理 MySQL 查询错误?
    处理 php 中的 mysql 查询错误非常重要。有多种方法可以做到这一点,包括使用 mysqli_error() 和 mysqli_errno() 函数来获取错误消息和代码,以及使用异...
    99+
    2024-05-11
    mysql php
  • Qt数据库应用中如何实现通用数据库采集
    小编给大家分享一下Qt数据库应用中如何实现通用数据库采集,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!一、前言数据库采集对应的就是上一篇文章的数据库同步,数据库同...
    99+
    2023-06-29
  • jQuery如何实现AJAX调用错误处理
    这篇文章主要为大家展示了“jQuery如何实现AJAX调用错误处理”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“jQuery如何实现AJAX调用错误处理”这篇文...
    99+
    2024-04-02
  • 如何在Android中实现通知栏
    今天就跟大家聊聊有关如何在Android中实现通知栏,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。一、设置通知内容//CHANNEL_ID,渠道ID,Android 8.0...
    99+
    2023-06-15
  • 如何在Android中实现多语言
    本篇文章给大家分享的是有关如何在Android中实现多语言,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。创建多语言包首先在onCreate方法中调用此方法查看上一次保存的是什么...
    99+
    2023-05-30
    android
  • 如何在Android 中实现scp操作
    目录SSHSCPSFTPAndroid中使用SCPSFTP 删除文件本文简单介绍用SSH库ganymed-ssh2在Android中实现scp操作。 SSH SSH是专为远程登录会...
    99+
    2024-04-02
  • php如何实现只在本地输出错误
    这篇文章主要讲解了“php如何实现只在本地输出错误”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“php如何实现只在本地输出错误”吧!首先,我们需要了解PHP的错误处理机制。在PHP中,所有的...
    99+
    2023-07-05
  • 如何在 Golang 中同时处理多个错误?
    非常抱歉,由于您没有提供文章标题,我无法为您生成一篇高质量的文章。请您提供文章标题,我将尽快为您生成一篇优质的文章。...
    99+
    2024-05-14
  • 如何在 Golang 中处理文件系统错误?
    非常抱歉,由于您没有提供文章标题,我无法为您生成一篇高质量的文章。请您提供文章标题,我将尽快为您生成一篇优质的文章。...
    99+
    2024-05-15
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作