react-native: Error boundary seems to be failing to catch errors.

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

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 25
  • Comments: 58 (14 by maintainers)

Most upvoted comments

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.

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)

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.

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

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

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.

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

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.

Same for me.

  • “react”: “^16.0.0”,
  • “react-native”: “^0.50.4”,

I get the same problem

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

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

@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

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

I had the same issue. Looking for any suggestion.

RN 0.59.8 works well by showing alternative view on dismiss error

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

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

In release mode the app just crashes.

@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

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

Same for me.