Target Platform:
iOS, android
Build tools:
create-react-native-app / expo locally, snack.expo.io online.
Use this code:
import React from 'react';
import { Text, Button } from 'react-native';
class ErrorBoundary extends React.Component {
state = { hasError: false }
componentDidCatch() {
this.setState({ hasError: true })
}
render() {
if (this.state.hasError) {
return <Text>Error in Component</Text>
}
return this.props.children
}
}
const Component = ()=> (
<Button
title="Throw Error!"
onPress={()=> { throw new Error() }}
/>
)
export default ()=> (
<ErrorBoundary>
<Component />
</ErrorBoundary>
)
It should behave like react in the browser and display the text "Error in Component".
In development mode the red error screen shows. In production the app crashes and restarts.
I get the same problem
I had the same issue. Looking for any suggestion.
Same for me.
Same for me here.
I don't think componentDidCatch() method is available in react-native right now. React native is not rely on fiber in current version.
componentDidCatch is available in RN 0.49.0-stable: https://github.com/facebook/react-native/blob/cd81eff5b891a0b5c528b4315423f157100b67e8/Libraries/Renderer/ReactNativeFiber-dev.js#L2715
This requires React 16.0.0-beta.5
@eliperkins My component still doesn't catch an error.
RN: 0.49.3
React: 16.0.0-beta.5
@JesperLekland Can you show your component? Note that only errors thrown within React lifecycle events will be captured.
I also have this issue. I was just testing this by throwing an error in the componendDidUpdate lifecycle method of a child component. I can catch the error, but it also pops up as a red screen.
I can reproduce with
"react": "16.0.0-beta.5",
"react-native": "0.49.3"
and
"react": "16.0.0",
"react-native": "0.50.0-rc.0"
componentDidCatch appears to trigger / update state as expected, but a red box displaying the component stack is displayed.
import React, { Component } from 'react';
import { Text, View } from 'react-native';
class ErrorBoundary extends React.Component {
state = { hasError: false };
componentDidCatch(error, info) {
this.setState({ hasError: true });
}
render() {
if (this.state.hasError) {
return <Text>Error in Component</Text>;
}
return <BadComponent />;
}
}
const BadComponent = () => {
throw new Error();
};
export default class App extends Component<{}> {
render() {
return (
<View>
<Text>Welcome to React Native!</Text>
<ErrorBoundary />
</View>
);
}
}
So is the "bug" here that the RedBox is displayed? It seems like if the error is caught and handled, there shouldn't be a RedBox, but I don't think that matches that the original issue was here...
That's all I'm seeing, at least. Happy to open a new issue if you'd prefer.
Here's the demo using expo SDK 22, which uses react 16.0.0-beta.5 and react-native 0.49:
https://snack.expo.io/Byc3n0A6-
Result: Uncaught Error: Error
@gunn Try this example https://snack.expo.io/@gusgard/error-boundaries-(componentdidcatch-example)
Remember that errors are caught in render but are not caught in event handlers.
If you test it in release mode, you will see the message "Error in Component", otherwise in dev mode on your device (without using Expo) tap dismiss in the error screen.
I am having the same issue.
Error gets caught in componentDidCatch, but red screen is still shown - in DEV mode, and app crashes - in PROD mode.
@gusgard I believe it should handle errors in any component lifecycle methods, not just render: https://codepen.io/anon/pen/eePzKX
In your snack example the componentDidCatch isn't actually firing (on error the child components of the error boundary are still rendered) Snack is using a modified version of https://github.com/gaearon/react-transform-catch-errors to provide the redboxing you're seeing, and the error is handled before it bubbles up to the error boundary
@bvaughn Would you happen to know if the RN renderer is calling console.error() at error boundaries? This would explain the redbox in dev and crash in prod (which is what console.error does).
Yes, console.error is called by default. The logic is here.
Thanks for the pointer, looks like we can override this with the injection interface.
I'm sorry, @ides. I believe my previous message was misleading. (It's been a while since I've worked with this code.)
ReactNativeRenderer injects a custom implementation of showDialog here. That implementation comes from ReactNativeFiberErrorDialog, and it always returns false to prevent ReactFiberErrorLogger from console.error logging the error (due to this check).
So it's still possible that console.error is called in other places within the React Native renderer, but I _don't_ think this is one of them. (You can search the renderer source for console.error references.) For example, if an error happens in the logCapturedError method, the renderer catches it and logs (via console.error).
Actually, it looks like this call to ExceptionsManager.handleException will result in console.error being called. This is outside of the core "renderer" code so I'm less familiar with it. 😄
Same for me.
the issue is that onPress is not part of the React render lifecycle. Error boundaries will only trigger when React is trying to render your components:
render() {
throw new Error('this will get caught');
}
or
render() {
return (
<Text>
{undefined.methodDoesNotExist()} // NPE
</Text>
hope that helps
Cole's right. I didn't read the original bug report until now, but it's describing expected behavior. From the React docs:
Error boundaries catch errors during rendering, in lifecycle methods, and in constructors of the whole tree below them.
Error boundaries do not catch errors for:
- Event handlers (learn more)
- Asynchronous code (e.g.
setTimeoutorrequestAnimationFramecallbacks)- Server side rendering
- Errors thrown in the error boundary itself (rather than its children)
This is still happening for us we are throwing from render and/or componentDidMount
To clarify. By "This" I mean that the red screen shows and the release build crashes, even though the componentDidCatch does actually run
Example: https://snack.expo.io/HJ4riE2ZG
@apolishch componentDidCatch doesn't prevent crashes, it just lets you catch the error and respond appropriately before the error goes unhandled. Per the example docs, set hasError to true in your error boundary component, and render a fallback ui if hasError is true. Right now, your error boundary is catching the error, but rendering the same error again
I'm attempting to create a 'fail whale' page that pops up whenever the app crashes.
No matter what it is throwing a red screen.
Here is a snack I made with the same issues:
https://snack.expo.io/BynlDr6-z
What am I doing wrong here?
Thanks!
@judgejab your code looks fine. What happens when you dismiss the red screen. In dev red screen still shows, just dismiss. Run in release mode and it won't show red screen
@stantoncbradley When I dismiss the red screen, I just get a fixed blank white screen.
In release mode the app just crashes.
@judgejab hmm code looks fine to me. What react/react native versions?
I'm using React 16.2 and React Native 0.49. I'm currently going to work through updating React Native to see if anything changes.
That being said, why isn't the Expo Snack working? It should be the most up to date?
It shows the red box with error as Cannot find variable abc.
render()
{
return (
<View style={{flex:1,backgroundColor:white }}>
<ScrollView style={{padding:10,flex: 1}}>
<ErrorBoundary>
<Text>{abc}</Text>
</ErrorBoundary>
</ScrollView>
</View>
);
}
import React, { Component } from 'react';
import {
View,
Text
} from 'react-native'
export default class errorBoundary extends Component {
constructor(props) {
super(props);
this.state = {
hasError:false,
} ;
}
componentDidCatch(eror,info)
{
alert("ff");
this.setState({ hasError: true });
}
render()
{
if (this.state.hasError) {
// You can render any custom fallback UI
return <Text>jbvi</Text>;
}
return this.props.children;
}
}
This is certainly resolved as of Expo 24 / RN 0.51: https://snack.expo.io/SyP0c39Xz
It may have been earlier, but I found the redbox behaviour on caught exceptions in dev pretty surprising.
For anyone else who runs into this, displaying a redbox does not mean that your error boundary isn't functioning. You can switch to prod (but lose all of the other dev goodies) or just dismiss the error and move on.
Same for me, react 16.2.0, react-native 0.51.0
@outpunk @shruti8597 I thought I had the same issue, but it might just be a misunderstanding in the way Error boundaries are working.
Taking @shruti8597 's example :
// component.js
export default class TopLevelComponent extends Component {
render() {
return (
<View style={{ flex: 1,
backgroundColor: white }}
>
<ScrollView style={{ padding: 10,
flex: 1 }}
>
<ErrorBoundary>
<Text>{abc}</Text>
</ErrorBoundary>
</ScrollView>
</View>
);
}
}
// ErrorBoundary.js
import React, { Component } from 'react';
import { View, Text } from 'react-native';
export default class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
};
}
componentDidCatch(eror, info) {
this.setState({ hasError: true });
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <Text>Oops</Text>;
}
return this.props.children;
}
}
This indeed is not caught.
But modifying it to the following makes it work properly :
// component.js
export default class TopLevelComponent extends Component {
render() {
return (
<View style={{ flex: 1,
backgroundColor: white }}
>
<ScrollView style={{ padding: 10,
flex: 1 }}
>
<ErrorBoundary>
<MyErroringComponent />
</ErrorBoundary>
</ScrollView>
</View>
);
}
}
class MyErroringComponent extends Component {
render () {
return (<Text>{abc}</Text>);
}
}
// ErrorBoundary.js
import React, { Component } from 'react';
import { View, Text } from 'react-native';
export default class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
};
}
componentDidCatch(eror, info) {
this.setState({ hasError: true });
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <Text>Oops</Text>;
}
return this.props.children;
}
}
@adrienthiery you're just forcing the state to render the error, the error needs to come from the actually wrapped child component. The whole idea is that the wrapper catches errors from the child component.
if you dismiss the red screen, you can see it working.
@stantoncbradley outside of dev mode [i.e in EXPO] results in this error https://github.com/expo/expo/issues/916
@bvaughn this is still the case outside of event handler caveat, you can throw an error in the render function of the throwing component and the behaviour is still the same.
This is not working properly for me with react-native 0.54.3; the error boundary catches the error, but the red box is showing, and when dismissing the red box I get a blank white screen. The fallback UI does not show.
after some tests, i found that if the error happens in child component componentDidCatch works(you should dismissing the red box), for example:
class Error extends Component{
render(){
throw new Error();
}
}
export default class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
};
}
componentDidCatch(eror, info) {
this.setState({ hasError: true });
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <Text>Oops</Text>;
}
return <Error />;
}
}
I can't get this to work properly either. If I just run a test as @succeed2011 shows, it works, but if an error is thrown down the road somehwere in my real life app, the error isn't caught.
I found out why I couldn't catch deeper down errors. There was a bug in [email protected] that was accidentally catching all errors in any react navigation screen, upgrading to 2.0.4 fixed it. Seems to me like error boundaries are working in the latest react native. 🎉
@MrLoh Can you please post the package.json file that works for you?
And one more question: does it work when testing in dev-mode (e.g. on a genymotion emulator)? Or does it require dev-mode to be turned off?
I think it only works in production or staging environments
react-native: 0.55.4
react-navigation: 2.2.5
I just catches erros in lyfecycle and render, not in other global js code.
For that use this:
if (!__DEV__) {
global.ErrorUtils.setGlobalHandler((error: Error, isFatal: boolean) => {
// report the error that occurred and raise a modal if it was fatal
reportError(error);
if (isFatal) showErrorAlert(error);
});
}
I also have problems with this.
From the replies in this thread: Do I understand correct that componentDidCatch is NOT supposed to catche errors that happen in a evet like handleSubmit from a child?
If so, which component do I need to use to also catche these errors?
As the docs mention, componentDidCatch catchers errors that happen during the render phase– (this includes the constructor, render, and the componentWill/componentDid lifecycles)– but not in other JS code.
@Spenhouet you can’t catch those errors in a component, you have to use the global catching code I posted above.
I also face to redbox with componentDidCatch during development.
I get redbox even if componentDidCatch correctly handle the error. It seems redbox is shown by the following code:
https://github.com/facebook/react-native/blob/v0.57.3/Libraries/Renderer/oss/ReactNativeRenderer-dev.js#L12194
It's different context from global error handler and I couldn't suppress redbox with ErrorUtils.setGlobalHandler.
I can continue to work dismissing redbox but I'd like to suppress it when developing error handler.
Do you happen to have any idea to suppress it?
My react-native version is v0.57.3
Me too. show the redbox error, show componentDidCatch alert in my code.
I have almost given up on ErrorBoundaries, they just don't work as described, (either that or the documentation needs seriously updating). Even the simplest examples, (including the docs) don't work. I feel perhaps the ErrorBoundary ecosystem in React is not yet fully mature?
Unless I am way off base, the following example doesn't work (React 16.6).
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = Â { hasError: false }
}
componentDidCatch(error ErrorInfo): void {
this.setState({ hasError: true });
}
render() {
if (this.state.hasError) {
return <div><h2>Oops, something went wrong!</h2></div>
}
return this.props.children
}
}
class WrappedChild extends React.Component {
componentDidMount() {
throw new Error("BOOM")
}
render() {
return (
<ErrorBoundary>
<h1>Im the Wrapped component with a Bug</h1>
</ErrorBoundary>
)
}
}
even using the suggested static getDerivedStateFromError(error) does not help:
Is it me, or.... ?
@stevematdavies Yes I think you have misunderstood a little. Try it like this:
class Child extends React.Component {
componentDidMount() {
throw new Error("BOOM")
}
render() {
return (
<h1>Im the Wrapped component with a Bug</h1>
)
}
}
class WrappedChild extends React.Component {
render() {
return (
<ErrorBoundary>
<Child/>
</ErrorBoundary>
)
}
}
Aha I see, yes then it was my misunderstanding, although, I still hold the docs could do with some refinement. So wrapped component must be an Element itself that throws, one can't simply wrap html from within an element.
Many thanks @gunn much appreciated.
I also see the red screen on expo dev mode. On production mode the app restart first and then it shows the error.
"react-native": "https://github.com/expo/react-native/archive/sdk-32.0.0.tar.gz",
"react": "16.5.0",
There is no issue here to my understanding this is the expected behavior:
I think the idea here is that in debug mode you want to know of any exception that is being thrown - even if caught, we can argue if this is a good approach or not but I don't think this is a bug.
My current experience is as follows:
RN 0.59.8 works well by showing alternative view on dismiss error
https://github.com/doochik/babel-plugin-transform-react-ssr-try-catch
I use that Babel plugin, basically it's to wrap render() method in React.Component with try-catch statement.
The behaviour in the code posted by the OP seems correct and different from the issues in the replies. The error is thrown in onPress which won't be caught by the error boundary.
Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. You may also label this issue as a "Discussion" or add it to the "Backlog" and I will leave it open. Thank you for your contributions.
Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please feel free to create a new issue with up-to-date information.
Most helpful comment
This is not working properly for me with
react-native0.54.3; the error boundary catches the error, but the red box is showing, and when dismissing the red box I get a blank white screen. The fallback UI does not show.