ComponentDidCatch isn't catching SVG Render errors - react-native

My team uses a general RenderErrorWrapper component everywhere. Looks basically like this
export class RenderErrorWrapper extends React.PureComponent<RenderErrorWrapperProps, RenderErrorWrapperState> {
constructor(props: RenderErrorWrapperProps) {
super(props)
this.state = { renderComponentSuccessful: true }
}
componentDidCatch(error: Error) {
this.setState({ renderComponentSuccessful: false })
}
render() {
if (!this.state.renderComponentSuccessful) {
this.props.fallbackComponent
}
return this.props.children
}
}
Now the thing is, this thing works. We know it works, it's a standard pattern for us, we see the errors this is catching, see the fallback UIs it displays.
Except.
We're also using the SvgUri component from react-native-svg-uri. Specifically the call we're doing is like
<SvgUri width={width} height={height} svgXmlData={xmlString} />
Now, if that xmlString isn't invalid, crazy things happen. We get an exception that bubbles all the way up, straight through our RenderErrorWrapper. We literally see the RenderErrorWrapper in the stack trace, but its ComponentDidCatch never gets called!
Invariant Violation: Text strings must be rendered within a <Text> component.
This error is located at:
in RNSVGGroup (at G.js:23)
in G (at Svg.js:127)
in RNSVGSvgView (at Svg.js:116)
in Svg (at react-native-svg-uri/index.js:168)
in RCTView (at View.js:45)
in View (at react-native-svg-uri/index.js:288)
in SvgUri (created by MyComponent)
in RenderErrorWrapper (created by MyComponent)
in RCTView (at View.js:45)
<< etc >>
I know react-native-svg-uri is an abandoned library at this point, unfortunately we're basically stuck with it. What I'm more concerned about is how this exception manages to miss our error handling framework entirely, since that could happen with other issues as well.
What makes this error special that it can bypass our error handling?

Figured out what's going on here: The problem is that RNSVGGroup is a Native component called by the SVG renderer, and Native errors skip your React Native error catch layers.
You can still catch and log the error but you have to use a native error handler to catch the native error first, and it won't save you from the Red Screen of Death. Alternatively, newer versions of react-native-svg might not have this issue, ours is pretty old.

Related

Invariant Violation: Calling synchronous methods on native modules is not supported in Chrome

I have forked this React library for use with React Native and have got it working by installing react-native-svg, use-elapsed-time and prop-types:
https://github.com/vydimitrov/react-countdown-circle-timer
However I now am not able to use the debugger:
Invariant Violation: Calling synchronous methods on native modules is
not supported in Chrome.
Consider providing alternative methods to expose this method in debug
mode, e.g. by exposing constants ahead-of-time.
This error is located at:
in CountdownCircleTimer (at AppRoot.js:118)
in AppRoot (at App.js:9)
in Provider (at App.js:8)
in App (at renderApplication.js:40)
in RCTView (at AppContainer.js:101)
in RCTView (at AppContainer.js:119)
I have searched high and low for any clues as to which package could be causing the error and I can only see the issue reported relating to react-native-device-info but this is not causing the problem. What does the error mean an how can I begin to debug this if there is such little information around about it?
This is temporary fix. This is working perfectly on my side. you have to edit this file
node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js
callNativeSyncHook(
moduleID: number,
methodID: number,
params: any[],
onFail: ?Function,
onSucc: ?Function,
): any {
const isDebuggingEnabled = (typeof atob !== 'undefined');
this.processCallbacks(moduleID, methodID, params, onFail, onSucc);
if(!isDebuggingEnabled)
{
return global.nativeCallSyncHook(moduleID, methodID, params);
}
}
you can also use patch-package to patch it permanently.
underlaying issue
For anyone else landing here from a Google search, things seem to have changed greatly since 2011 in regards to this issue (which isn't surprising).
If you try #MuhammadNuman's solution you will just get a different error about global.nativeCallSyncHook not being a function.
The workaround I'm using for this is to simply return anything else from callNativeSyncHook. E.g.:
callNativeSyncHook(
moduleID: number,
methodID: number,
params: any[],
onFail: ?Function,
onSucc: ?Function,
): any {
const isDebuggingEnabled = (typeof atob !== 'undefined');
this.processCallbacks(moduleID, methodID, params, onFail, onSucc);
if (!isDebuggingEnabled) {
return "Meh, I have no idea what's going on here, but at least this gets rid of the annoying error!";
}
}
I'd be very interested to know if there's a "proper" way to fix this post 2011, but for now this is good enough for me.
There is no need to modify or patch any npm package for this anymore.
The issue is fixed by react native 0.66.
Ref:
Stack Overflow Answer
Github Issue/Comment
I am using RN 0.67.3. This error was found in Galaxy S22 in debug mode.
I rerun yarn android and the error has gone.

React Native app does not crash as expected

We have been investigating issues in our app where we just see a white screen and no crash is logged. Goes for both Android and iOS
The issue seem to be related to the use of async/await or promises.
We have found that we can reproduce the white screen issue locally when using async/await or promises.
The issue can be reproduced with the following code (tried to simplify it as much as possible). Replace App.js in a new project with the following:
import React, {Fragment} from 'react';
import {Text} from 'react-native';
class App extends React.Component {
state = {isMounted: false};
async componentDidMount() {
await Promise.resolve(1);
this.setState({isMounted: true});
}
render() {
if (this.state.isMounted) {
this.state.isMounted.test();
}
return (
<Fragment>
<Text>test</Text>
</Fragment>
);
}
}
export default App;
I also pushed a test project on github
I would expect the app to crash as isMounted does not contains a test method.
When running locally I get the red box stating that an error occurred as expected
TypeError: this.state.isMounted.test is not a function. (In 'this.state.isMounted.test()', 'this.state.isMounted.test' is undefined)
This error is located at:
in App (at renderApplication.js:40)
in RCTView (at View.js:35)
in View (at AppContainer.js:98)
in RCTView (at View.js:35)
in View (at AppContainer.js:115)
in AppContainer (at renderApplication.js:39)
...
but when running a release build the app does not crash and no error is reported. This makes it very hard for us to find and fix issues like this as we are totally blind.
If I comment out
await Promise.resolve(1);
It crash as expected in a release build
React Native version: 0.60.5
Are we wrong in assuming that the above code should result in an app crash?
Found that error-boundaries was the missing piece. Implementing an error boundary on the root level allows us to manually log these errors to our crash handling system.
As a side note I can also mention that if you use redux-saga and you throw an error, the error by default seem to be swallowed. So if you want to handle these uncaught errors you can implement the onError option in createSagaMiddleware

React-native lifecycle methods warning: " componentWillReceiveProps is deprecated and will be removed in the next major version"

While using the life-cycle methods in react-native, I have encountered the following warning messages in the console as well as in the emulator:
Warning: componentWillReceiveProps is deprecated and will be removed in the next major version. Use static getDerivedStateFromProps instead.
Warning: componentWillMount is deprecated and will be removed in the next major version. Use componentDidMount instead. As a temporary workaround, you can rename to UNSAFE_componentWillMount.
The warnings are even displayed annoyingly in the emulator as follows:
The problem arises whenever i dismiss the warning messages in the emulator, the application crashes, hence i have to restart the application again.
What can I do about this?
Also, despite the warning messages, I have never used the method "componentWillReceiveProps". However, I have made use of the method "componentWillMount". What could be the cause of receiving warning message even related to method "componentWillReceiveProps"?
I think it does not affect your application, it's just annoying.
Hide it by add below into index.js:
import { YellowBox } from 'react-native';
YellowBox.ignoreWarnings([
'Warning: componentWillMount is deprecated',
'Warning: componentWillReceiveProps is deprecated',
'Module RCTImageLoader requires',
]);
These deprecation notices was not due to land in React Native 0.54 but was by mistake, you can upgrade React Native to 0.54.1 and see that these messages are gone.
Read more here.
Just replace
componentWillReceiveProps(nextProps) { ... }
with
static getDerivedStateFromProps(nextProps, prevState) { ... }
and use prevState as your current state.
remember, this method should return an object to update the state or null to update nothing and doesn't have access to the component instance. something like redux.
see this

How to catch component unmount caused by live reload

[edit - I thought I was using Hot Reloading, but I am actually using Live Reload]
I have a native plugin that needs to do some clean up each time it is finished with. Basically I want to prevent these errors:
Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(..)
componentWillUnmount() doesn't get called.
Live reloading will restart the app and load the app back to the initial route when a file changes. ComponentWillUnmount won't be called.
When you reload, what happens behind the scenes is that the react context is getting destroyed, and a new one is being created.
That error get's thrown whenever a Native Module is trying to do work, by using the old react context.
You could use something like this:
Error Boundary in react
Just wrap the error prone code inside the ErrorBoundary component, e.g.
<ErrorBoundary><childComponentCausingError></ErrorBoundary>
And in the ErrorBoundary component, you could just catch the error in componentDidCatch = (error, info) => {
}
componentDidCatch() is a lifecycle method in React.

I'm getting this error in my simulator when trying to render React Native app

This is my first time using react native. I am following a tutorial but my results are different than the tutorial. I've triple checked my code and its exactly as its shown but I'm getting this error. Any ideas?
// Import a library to help create a Component
import React from 'react';
import { Text, AppRegistry } from 'react-native';
// Create a Component
const App = () => (
<Text>Some Text</Text>
);
// Render it to the device
AppRegistry.registerComponent('albums', () => App);
The error is
Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: object. You likely forgot to export your component from the file it's defined in.
Check the render method of `AwakeInDevApp`.
in AwakeInDevApp (at registerRootComponent.js:21)
in ExponentRootComponent (at renderApplication.js:35)
in RCTView (at View.js:128)
in View (at AppContainer.js:93)
in RCTView (at View.js:128)
in View (at AppContainer.js:92)
in AppContainer (at renderApplication.js:34)
Screenshot of Error in Simulator
Try to change 'albums' to 'AwakeInDevApp', it must be your project name when you run react-native init YourProjectName