Обработка исключений React Native

исходный код React Native

Оригинальный адрес:GitHub.com/hu J IA oh J/ Первоначально…

Когда на странице React Native есть ошибка:

1. В режиме разработки появится страница с красным фоном, показывающая текущую информацию об ошибке кода.

2. В пакетном режиме появится белый экран или флешбэк

режим разработки

rn_dev_error

пакетный режим

rn_bundle_error

В производственной среде все приложение представляет собой белый экран или мигает из-за ненормальной страницы RN, а взаимодействие с пользователем неудовлетворительное, поэтому аномалию следует зафиксировать и обработать, чтобы улучшить взаимодействие с пользователем.

Для захвата и обработки исключений страниц RN в основном используются два метода:

1. Границы ошибки реакции

2. Модуль React Native ErrorUtils

Границы ошибки реакции

Границы ошибок React (компоненты границ исключений) — это новая концепция, введенная в React 16. Чтобы избежать исключений пользовательского интерфейса в компонентах React, которые вызывают исключения для всего приложения.

Друзья, незнакомые с граничными компонентами исключений React, могут прочитать мою статью:Обработка исключений React из исходного кода

Вот краткое введение:

Границы ошибок — это компонент React, который фиксирует исключения js, сгенерированные всеми компонентами в его дереве подкомпонентов, и отображает указанный нижний пользовательский интерфейс для замены рассматриваемого компонента.

Он может перехватывать исключения в функциях жизненного цикла подкомпонентов, включая функции конструктора и рендеринга.

вместо того, чтобы поймать следующее исключение:

  • Обработчики событий
  • Асинхронный код (асинхронный код, такой как setTimeout, promise и т. д.)
  • Рендеринг на стороне сервера
  • Ошибки, возникающие в самой границе ошибки (а не в ее дочерних элементах)

Таким образом, все исключения в жизненном цикле компонента могут быть зафиксированы с помощью компонента границы исключения, а нижний пользовательский интерфейс может быть визуализирован, чтобы предотвратить появление белого экрана или флэшбэка в приложении и улучшить взаимодействие с пользователем. нижний пользовательский интерфейс для облегчения поиска и устранения неисправностей.

Перейдите непосредственно к коду:

with_error_boundary.js

...
function withErrorBoundary(
    WrappedComponent: React.ComponentType <CatchCompProps> ,
    errorCallback: Function,
    allowedInDevMode: boolean,
    opt: Object = {}) {
    return class extends React.Component <CatchCompProps, CatchCompState> {
        state = {
            error: null,
            errorInfo: false,
            visible: false,
        }
        componentDidCatch(error: Error, errorInfo: any) {
            this.setState({
                error,
                errorInfo,
                visible: true,
            })
            errorCallback && errorCallback(error, errorInfo)
        }
        handleLeft = () => {
            ...
        }
        render() {
            const { title = 'Unexpected error occurred', message = 'Unexpected error occurred' } = opt
            return (
                this.state.visible && (allowedInDevMode ? true : process.env.NODE_ENV !== 'development') ? (
                <Modal 
                    visible
                    transparent
                    animationType={'fade'}>
                    <View style={styles.container}>
                        <View style={styles.header}>
                        <NavBar
                            title={title}
                            leftIcon={'arrow-left'}
                            handleLeft={this.handleLeft}/>
                        </View>
                        <View style={styles.info}>
                            <Text>{message}</Text>
                        </View> 
                        <ScrollView style={styles.content}>
                            <Text> { this.state.error && this.state.error.toString()} </Text>
                            <Text> { this.state.errorInfo && this.state.errorInfo.componentStack } </Text> 
                        </ScrollView>
                    </View>
                </Modal>
                ) : <WrappedComponent {...this.props} />
            );
        }
    }
}

export default withErrorBoundary;

Вышеупомянутый компонент React более высокого порядка, и возвращаемый компонент определяетcomponentDidCatchФункция жизненного цикла, когда в ее дочернем компоненте возникает исключение, это будет выполненоcomponentDidCatchФункция жизненного цикла, отображающая нижний пользовательский интерфейс

использовать

...
import withErrorBoundary from 'rn_components/exception_handler/with_error_boundary.js';
...
class ExceptionHandlerExample extends React.Component {
    state = {
        visible: false,
    }
    catch = () => {
        console.log('catch');
        this.setState({
            visible: true,
        });
    }
    render () {
        if (this.state.visible) {
            const a = d
        }
        return (
            <View style={styles.container}>
                <Navbar 
                    title={'Exception Handler'}
                    handleLeft={() => this.props.history.go(-1)}/>
                <View style={styles.content}>
                    <TouchableOpacity onPress={this.catch}>
                        <View>
                            <Text>Click me</Text>
                        </View>
                    </TouchableOpacity>
                </View>
            </View>
        );
    }
}
// 异常边界组件的使用
export default withErrorBoundary(ExceptionHandlerExample, (error, errorInfo) => {
    console.log('errorCallback', error, errorInfo);
}, true);

Мы также сказали выше, что компоненты границы исключений могут захватывать исключения в функциях жизненного цикла подкомпонентов, включая конструкторы и функции рендеринга.

вместо того, чтобы поймать следующее исключение:

  • Обработчики событий
  • Асинхронный код (асинхронный код, такой как setTimeout, promise и т. д.)
  • Рендеринг на стороне сервера
  • Ошибки, возникающие в самой границе ошибки (а не в ее дочерних элементах)

Поэтому вам нужно использовать модуль React Native ErrorUtils для перехвата и обработки этих исключений.

Модуль React Native ErrorUtils

React Native ErrorUtils — это модуль, отвечающий за управление исключениями на страницах RN, аналогичный функции window.onerror на веб-страницах.

Во-первых, давайте посмотрим, как использовать React Native ErrorUtils для асинхронного захвата и обработки непосредственно в коде:

error_guard.js

const noop = () => {};

export const setJSExceptionHandler = (customHandler = noop, allowedInDevMode = false) => {
    if (typeof allowedInDevMode !== "boolean" || typeof customHandler !== "function") {
        return;
    }
    const allowed = allowedInDevMode ? true : !__DEV__;
    if (allowed) {
        // !!! 关键代码
        // 设置错误处理函数
        global.ErrorUtils.setGlobalHandler(customHandler);
        // 改写 console.error,保证报错能被 ErrorUtils 捕获并调用错误处理函数处理
        console.error = (message, error) => global.ErrorUtils.reportError(error);
    }
};

export const getJSExceptionHandler = () => global.ErrorUtils.getGlobalHandler();

export default {
    setJSExceptionHandler,
    getJSExceptionHandler,
};

Приведенный выше код ключа состоит всего из двух строк, отмеченных в комментариях.

использовать

import { setJSExceptionHandler } from './error_guard';
import { Alert } from 'react-native';

setJSExceptionHandler((e, isFatal) => {
    if (isFatal) {
        Alert.alert(
            'Unexpected error occurred',
            `
            ${e && e.stack && e.stack.slice(0, 300)}...
            `,
            [{
                text: 'OK',
                onPress: () => {
                    console.log('ok');
                }
            }]
        );
    } else {
        console.log(e);
    }
}, true);

Это очень просто в использовании, давайте посмотримErrorUtilsисходный код модуля

Исходный код ErrorUtils

Исходный код этой статьи — это код основной ветки хранилища React Native, извлеченный 10 сентября 2018 г.

error_guard.js

Сначала посмотрите на определение ErrorUtils, расположение исходного кода:Libraries/polyfills/error_guard.js

let _inGuard = 0;

let _globalHandler = function onError(e) {
  throw e;
};

const ErrorUtils = {
  setGlobalHandler(fun) {
    _globalHandler = fun;
  },
  getGlobalHandler() {
    return _globalHandler;
  },
  reportError(error) {
    _globalHandler && _globalHandler(error);
  },
  reportFatalError(error) {
    _globalHandler && _globalHandler(error, true);
  },
  ...
};

global.ErrorUtils = ErrorUtils;

Выше показан только метод, который мы использовали, мы можем видеть, что мы переписалиconsole.error,Прямо сейчас(message, error) => global.ErrorUtils.reportError(error)В конечном итоге казнен_globalHandler

Таким образом, вы можете захватить все используемыеconsole.errorисключение, давайте посмотрим, где в исходном коде React Native ErrorUtils используется для захвата и обработки исключений.

MessageQueue.js

приходитьMessageQueueИсходный код, расположение:Libraries/BatchedBridge/MessageQueue.js

__guard(fn: () => void) {
    if (this.__shouldPauseOnThrow()) {
        fn();
    } else {
        try {
            fn();
        } catch (error) {
            ErrorUtils.reportFatalError(error);
        }
    }
}

Мы можем видеть выше__guardиспользуемый методtry...catch...Охраняйте выполнение функции, при возникновении исключения она будет вызванаErrorUtils.reportFatalError(error);обрабатывать ошибки

использовал__guardМеста здесь не указаны, можем посмотретьMessageQueueГде находится этот модуль в РН

Поскольку я не видел систематически исходный код RN, я нашел в Интернете картинку, которая представляет связь между Native и JS.MessageQueueСвязь между Native и JS — важный модуль

BatchedBridge.js

приходитьBatchedBridgeИсходный код, расположение:Libraries/BatchedBridge/BatchedBridge.js

'use strict';

const MessageQueue = require('MessageQueue');

const BatchedBridge = new MessageQueue();

Object.defineProperty(global, '__fbBatchedBridge', {
  configurable: true,
  value: BatchedBridge,
});

module.exports = BatchedBridge;

Студенты, знакомые с RN, должны знать, чтоBatchedBridgeЭто ключевой модуль связи между Native и JS, Из приведенного выше исходного кода мы можем знать, чтоBatchedBridgeфактическиMessageQueueпример

так вMessageQueueИспользование ErrorUtils в модуле может перехватывать все исключения в процессе связи и вызывать_globalHandlerиметь дело с

Все приведенные выше коды можно посмотреть в разработанном мной проекте библиотеки компонентов РН:rn_components ExceptionHandler, библиотека компонентов только начала создаваться и будет продолжать улучшаться в будущем.

напиши в конце

Вышеизложенное - это моя часть обработки исключений React Native, я надеюсь, что она может быть полезна тем, кто в ней нуждается~~~

Если вам понравилась моя статья, вы можете перейтимой личный блогнажмите звездочку ⭐️