Original address: github.com/HuJiaoHJ/bl…
React Native page error:
1. In development mode, a page with a red background will appear, showing the error information of the current code
2. In bundle mode, a blank screen or a flash rollback is displayed
Development mode
Bundle model
In the production environment, the entire APP displays a blank screen or blinks due to an EXCEPTION on an RN page. Therefore, you need to capture and handle exceptions to improve user experience
There are two main methods to catch and handle exceptions on RN pages:
1, React Error Boundaries
React Native ErrorUtils module
React Error Boundaries
React Error Boundaries are a new concept introduced in React 16 to prevent UI exceptions in React components from causing app-wide exceptions
React exception handlers are not familiar with the React exception boundary component
Here is a brief introduction:
Error Boundaries are the React component that captures js exceptions generated by all components of its subcomponent tree and renders the specified pocket UI to replace the offending component
It catches exceptions in child component lifecycle functions, including constructors and render functions
The following exceptions cannot be caught:
- Event Handlers
- Asynchronous code (e.g. SetTimeout, promise, etc.)
- Server Side Rendering (Server-side Rendering)
- Error thrown in the error boundary itself (rather than its children)
Therefore, the exception boundary component can be used to capture all anomalies within the life cycle of the component and render the bottom of the pocket UI to prevent the APP from going blank or flashing back and improve the user experience. The user can also be guided to feed back screenshots of problems in the bottom of the pocket UI to facilitate the investigation and repair of problems
Directly on the code:
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 = (a)= >{... } 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;
Copy the code
ComponentDidCatch () {React} componentDidCatch () {React} componentDidCatch () {React} componentDidCatch () {React} componentDidCatch () {React
use
. import withErrorBoundaryfrom 'rn_components/exception_handler/with_error_boundary.js'; . class ExceptionHandlerExample extends React.Component { state = {visible: false,}catch = (a)= > {
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);Copy the code
As mentioned above, exception bound components can catch exceptions in child component lifecycle functions, including constructors and render functions
The following exceptions cannot be caught:
- Event Handlers
- Asynchronous code (e.g. SetTimeout, promise, etc.)
- Server Side Rendering (Server-side Rendering)
- Error thrown in the error boundary itself (rather than its children)
Therefore, use the React Native ErrorUtils module to catch and handle these exceptions
React Native ErrorUtils module
React Native ErrorUtils is a module that manages exceptions in RN pages. Its function is similar to window.onerror in Web pages
React Native ErrorUtils = React Native ErrorUtils
error_guard.js
const noop = (a)= > {};
export const setJSExceptionHandler = (customHandler = noop, allowedInDevMode = false) = > {
if (typeofallowedInDevMode ! = ="boolean" || typeofcustomHandler ! = ="function") {
return;
}
const allowed = allowedInDevMode ? true : !__DEV__;
if (allowed) {
/ /!!!!!! The key code
// Set the error handler
global.ErrorUtils.setGlobalHandler(customHandler);
// Overwrite console.error to ensure that ErrorUtils catches the error and calls the error handler
console.error = (message, error) = >global.ErrorUtils.reportError(error); }};export const getJSExceptionHandler = (a)= > global.ErrorUtils.getGlobalHandler();
export default {
setJSExceptionHandler,
getJSExceptionHandler,
};
Copy the code
The key code above is just two lines, indicated in the comment
use
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: (a)= > {
console.log('ok'); }}]); }else {
console.log(e); }},true);
Copy the code
Use is very simple, let’s take a look at the ErrorUtils module source
ErrorUtils source
The React Native repository master branch was installed on September 10, 2018
error_guard.js
Look first at ErrorUtils definition, source location: 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;Copy the code
Only shows that we use the method of above, we can see we rewrite the console. The error, namely (message, error) = > global. ErrorUtils. ReportError (error), Finally, the _globalHandler is executed
The React Native source code uses ErrorUtils to catch all exceptions using console.error
MessageQueue.js
Came to the MessageQueue source, location: Libraries/BatchedBridge/MessageQueue. Js
__guard(fn: (a)= > void) {
if (this.__shouldPauseOnThrow()) {
fn();
} else {
try {
fn();
} catch(error) { ErrorUtils.reportFatalError(error); }}}Copy the code
We can see that the __guard method uses a try… catch… For the implementation of the function, when an exception occurs, will call ErrorUtils. ReportFatalError (error); Handle errors
Where __guard is used is not listed here, but where is the MessageQueue module in RN
Since we have not read the source code of RN systematically, we found a map on the Internet to introduce the communication between Native and JS. We can see that MessageQueue is a very important module in the communication between Native and JS
BatchedBridge.js
Came to BatchedBridge source, location: 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;
Copy the code
Those familiar with RN should know that BatchedBridge is a key module for communication between Native and JS. From the source code above, we can know that BatchedBridge is actually an instance of MessageQueue
So using ErrorUtils in the MessageQueue module catches all communication exceptions and calls _globalHandler to handle them
All of the above code can be viewed in the project of my RN component library: RN_Components ExceptionHandler. The component library is just under construction and will continue to be improved
Write in the last
React Native exception handling
If you like my articles, go to my personal blog star ⭐️