Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error boundary seems to be failing to catch errors. #15571

Closed
gunn opened this issue Aug 20, 2017 · 58 comments
Closed

Error boundary seems to be failing to catch errors. #15571

gunn opened this issue Aug 20, 2017 · 58 comments
Labels
Bug JavaScript Stale There has been a lack of activity on this issue and it may be closed soon.

Comments

@gunn
Copy link

gunn commented Aug 20, 2017

Environment

  1. react-native: 0.47.1
  2. react: 16.0.0-alpha.12
  3. node: v8.3.0
  • Target Platform:
    iOS, android

  • Build tools:
    create-react-native-app / expo locally, snack.expo.io online.

Steps to Reproduce

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>
)

Expected Behavior

It should behave like react in the browser and display the text "Error in Component".

Actual Behavior

In development mode the red error screen shows. In production the app crashes and restarts.

Reproducible Demo

https://snack.expo.io/ryHYYfPdZ

@chuckwu0
Copy link

I get the same problem

@luisfmsouza
Copy link

I had the same issue. Looking for any suggestion.

@tumanov-alex
Copy link

Same for me.

@clementdevosmc
Copy link

Same for me here.

@timwangdev
Copy link
Contributor

timwangdev commented Oct 4, 2017

I don't think componentDidCatch() method is available in react-native right now. React native is not rely on fiber in current version.

@eliperkins
Copy link
Contributor

componentDidCatch is available in RN 0.49.0-stable:

return void instance.componentDidCatch(capturedError.error, info);

This requires React 16.0.0-beta.5

@JesperLekland
Copy link

JesperLekland commented Oct 17, 2017

@eliperkins My component still doesn't catch an error.

RN: 0.49.3
React: 16.0.0-beta.5

@eliperkins
Copy link
Contributor

@JesperLekland Can you show your component? Note that only errors thrown within React lifecycle events will be captured.

@wwwdata
Copy link

wwwdata commented Oct 24, 2017

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.

@tcdavis
Copy link

tcdavis commented Oct 25, 2017

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>
    );
  }
}

@eliperkins
Copy link
Contributor

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...

@tcdavis
Copy link

tcdavis commented Oct 25, 2017

That's all I'm seeing, at least. Happy to open a new issue if you'd prefer.

@gunn
Copy link
Author

gunn commented Oct 26, 2017

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

@gusgard
Copy link
Contributor

gusgard commented Nov 21, 2017

@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.

@easybird
Copy link

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.

  • "react": "^16.1.1",
  • "react-native": "^0.49.4",

@tcdavis
Copy link

tcdavis commented Nov 28, 2017

@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

@ide
Copy link
Contributor

ide commented Nov 28, 2017

@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).

@bvaughn
Copy link
Contributor

bvaughn commented Nov 28, 2017

Yes, console.error is called by default. The logic is here.

@ide
Copy link
Contributor

ide commented Nov 28, 2017

Thanks for the pointer, looks like we can override this with the injection interface.

@bvaughn
Copy link
Contributor

bvaughn commented Nov 28, 2017

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).

@bvaughn
Copy link
Contributor

bvaughn commented Nov 28, 2017

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. 😄

@Diwei-Chen
Copy link

Same for me.

  • "react": "^16.0.0",
  • "react-native": "^0.50.4",

@stantoncbradley
Copy link

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

@bvaughn
Copy link
Contributor

bvaughn commented Dec 6, 2017

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. setTimeout or requestAnimationFrame callbacks)
  • Server side rendering
  • Errors thrown in the error boundary itself (rather than its children)

@apolishch
Copy link

apolishch commented Dec 11, 2017

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

@stantoncbradley
Copy link

@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

@mattcleary55
Copy link

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!

@stantoncbradley
Copy link

@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

@mattcleary55
Copy link

@stantoncbradley When I dismiss the red screen, I just get a fixed blank white screen.

In release mode the app just crashes.

@stantoncbradley
Copy link

@judgejab hmm code looks fine to me. What react/react native versions?

@MrLoh
Copy link

MrLoh commented May 28, 2018

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.

@MrLoh
Copy link

MrLoh commented May 31, 2018

I found out why I couldn't catch deeper down errors. There was a bug in react-navigation@2.0.1 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. 🎉

@rahamin1
Copy link

@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?

@MrLoh
Copy link

MrLoh commented Jul 14, 2018

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);
	});
}

@Spenhouet
Copy link

Spenhouet commented Aug 29, 2018

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?

@bvaughn
Copy link
Contributor

bvaughn commented Aug 29, 2018

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.

@MrLoh
Copy link

MrLoh commented Aug 30, 2018

@Spenhouet you can’t catch those errors in a component, you have to use the global catching code I posted above.

@tomoyukim
Copy link

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

@Kennytian
Copy link

Me too. show the redbox error, show componentDidCatch alert in my code.

@stiofand
Copy link

stiofand commented Jan 31, 2019

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.... ?

@gunn
Copy link
Author

gunn commented Jan 31, 2019

@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>
    )
  }
}

@stiofand
Copy link

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.

@brunoreis
Copy link

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",

@lyair1
Copy link

lyair1 commented Mar 13, 2019

There is no issue here to my understanding this is the expected behavior:

  1. You wrap the component
  2. The error gets caught and the alternative view is being shown
  3. On release mode the user will see the alternative view
  4. On debug mode you'll still see the red error screen but you can dismiss it and see the alternative view.

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.

@FrankGoortani
Copy link

My current experience is as follows:

  1. You wrap the component
  2. The error gets caught and the alternative view is being shown
  3. On release mode the user will see the alternative view
  4. On debug mode you'll still see the red error screen - you can dismiss it but you don't see the alternative view.

@nicolashemonic
Copy link

RN 0.59.8 works well by showing alternative view on dismiss error

@dwicao
Copy link

dwicao commented May 29, 2019

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.

@satya164
Copy link
Contributor

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.

@stale
Copy link

stale bot commented Aug 29, 2019

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.

@stale stale bot added the Stale There has been a lack of activity on this issue and it may be closed soon. label Aug 29, 2019
@stale
Copy link

stale bot commented Sep 5, 2019

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.

@stale stale bot closed this as completed Sep 5, 2019
@facebook facebook locked as resolved and limited conversation to collaborators Sep 5, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Bug JavaScript Stale There has been a lack of activity on this issue and it may be closed soon.
Projects
None yet
Development

No branches or pull requests