How to implement Error Boundaries In React Native

Error boundaries are React components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI instead of the crashed component tree that crashed.

Why Should You Use Them? 🤔

Official docs can be read here:

Unmounting the whole React component tree means that if we don’t catch errors, the user will see an empty white screen. Most of the time, without having any feedback so it will hamper the user experience. So this problem should be fixed with the help of Error Boundaries.

Implementation:

Error Boundaries are class components. With the help of Error Boundaries, we can catch types of error, error info and display fallback UI.

export default class AppContainer extends Component {

  render() {

    return (

      <>

        <ErrorBoundries>

         {/* Rest of code */}

        </ErrorBoundries>

      </>

    );

  }

 }

Error Boundaries catch errors in their child components, during the rendering phase. And if any error is found it will give error details and fallback UI with help of the below lifecycle methods.

To achieve Error Boundaries, we need to add two lifecycle methods to the component.

getDerivedStateFromError(error):

This static lifecycle method is invoked when an error occurs, and that error can originate from any child component of the error boundary.

We can update the state of the component by returning a state like hasError:true, this will re-render the component and display fallback UI with help of conditional rendering.

componentDidCatch(error, errorInfo) :

This lifecycle method is optional. It is invoked after an error has been thrown by the descendant component. offers additional parameters, the error is for getting the type of error and errorInfo is an object with componentStack key which contains information where the error originally occurred.

For example, we have a sample counter app. If the value of the counter is negative, then we are throwing a JavaScript error. Let’s see how error boundaries have tracked errors. Also, we are showing error info and error track details with the help of componentDidCatch (as mentioned above).

1. We have added components as a child component in error boundaries

AppContainer.js

export default class AppContainer extends Component {

  render() {

    return (

      <>

        <Text>Counter App</Text>

        <ErrorBoundries>

             <CounterApp />

        </ErrorBoundries>

      </>

    );

  }

 }

2. If the counter value is negative then we are throwing a javascript error as below

if (state < 0) {

   throw new Error('Value should be Postitive');

  }

CounterApp.js

 import React, {useStatefrom 'react';

 import {ViewTextPressablefrom 'react-native';

 export default function CounterApp() {

  const [statesetState] = useState(0);

  const counterAdd = () => {

    setState(prevState => prevState + 1);

  };

  const counterRemove = () => {

    setState(prevState => prevState - 1);

  };

  if (state < 0) {

    throw new Error('Value should be Postitive');

  }

  return (

    <>

      <View

        style={{

          flex: 1,

          justifyContent: 'center',

          alignItems: 'center',

          flexDirection: 'row',

        }}>

        <Pressable

          onPress={() => {

            counterAdd();

          }}

          android_ripple={{color: 'silver'}}

          style={{

            paddingHorizontal: 20,

            paddingVertical: 6,

            backgroundColor: 'silver',

          }}>

          <Text> + </Text>

        </Pressable>

        <Text style={{paddingHorizontal: 6}}> {state} </Text>

        <Pressable

          onPress={() => {

            counterRemove();

          }}

          android_ripple={{color: 'silver'}}

          style={{

            paddingHorizontal: 20,

            paddingVertical: 6,

            backgroundColor: 'silver',

          }}>

          <Text> - </Text>

        </Pressable>

      </View>

    </>

  );

 }

3. Then will add a component that we added as a parent component.

export default class ErrorBoundries extends Component {

  constructor(props) {

    super(props);

    this.state = {

      hasError: false,

      error: '',

      errorInfo: '',

    };

  }

  static getDerivedStateFromError(error) {

    return {hasError: true};

  }

  componentDidCatch(errorerrorInfo) {

    console.log('Error: ' + error);

    console.log('Error Info: ' + JSON.stringify(errorInfo));

    this.setState({

      error: error,

      errorInfo: errorInfo,

    });

  }

  render() {

    if (this.state.hasError) {

      return (

        <View

          style={{

            flex: 1,

            justifyContent: 'center',

            alignItems: 'center',

          }}>

          <Text>Oops!!! Something went wrong..</Text>

          <Text>Error: {this.state.error.toString()}</Text>

          <Text>Error Info: {JSON.stringify(this.state.errorInfo)}</Text>

        </View>

      );

    }

    return this.props.children;

  }

 }

So we have Implemented Error Boundaries in React Native

The Problem without Error Boundaries :

Javascript errors corrupt component’s internal state and it will cause errors on the next renders, If any run time error occurs react will unmount the whole component tree and show the errors.

Important Notes:

  1. Error Boundaries do not catch errors for Event handlers, Asynchronous code (for ex. setTimeout), Server-side rendering.
  2. Error boundaries do not catch errors for Errors thrown in the error boundary itself.

Conclusion

This article is helpful for developers who want to implement the error boundaries in React Native components. Implementing an error boundary prevents unexpected app behavior and ultimately throws users out of the app upon an error.

Also, readers can find a full example with the help of Github link:

Happy Coding!! 🙂🙂

Keep Reading

Keep Reading

Let's get in touch!