Error handling in React Native

Error handling in React Native 1

If you have built a React app at any point in your programming career, you probably have experienced an error at some point that was cryptic and did not provide any meaningful context on what actually happened.

This probably means the error occurred at some point in the application and our React component did not handle the error gracefully, well mostly because it was none of its business to make sure the entire app holds.

A common problem in React JS is that if a part of a component breaks, the whole application is broken. A JavaScript error in a part of the UI shouldn’t break the whole app. To solve this problem for React users, React 16 introduces a new concept of an “error boundary”.

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 component tree that crashed. Error boundaries catch errors during rendering, in lifecycle methods, and in constructors of the whole tree below them.

Error screens

Before we dig deeper let’s have a look at some error screens of some famous applications. These are the error screens(fallback UI) of some applications, which will be displayed when something goes wrong in their application.

Error handling in React Native 2

Developers spend 50% of their time finding errors and fixing them. And errors in your application will naturally lead to unhappy customers, which in turn leads to unhappy management and which in turn makes the developer unhappy. So we need a better approach to find errors, track them and solve them. If we can’t solve them, show some fallback UI which will not disrupt the user experience.

What is the problem in React till now?

  • White Screen of death ⚰️(Error in one small component renders a blank page)
  • Whole React component tree is crashed 🌲.
  • Very difficult to track the issue and debug the code in production 😥

Traditional Ways of capturing errors

1.window.onerror

A window has a callback function called onerror, which will get called if something goes wrong in the browser’s window.

Error handling in React Native 3

Cons:

  • Not supported by all the browsers
  • Does not capture Cryptic “Script error”

“Script error”: Happens in Firefox, Safari, and Chrome when an exception violates the browser’s same-origin policy – i.e. when the error occurs in a script that’s hosted on a domain other than the domain of the current page.

  1. try-catch

Enclose the part of the code in try-catch block.

Error handling in React Native 4

Cons:

  • It only works with imperative code
  • Doesn’t capture error due to the state change in react component
  1. Promise-Catch

Every guarantee Ought to Be followed closely by a grab to Allow You to handle the exceptions.

  1. ComponentDidCatch

ComponentDidCatch became a part of this React 16 lifecycles. It means that once an error happens on a child’s part, the parent could catch & handle it gracefully. The good news is that by componentDidCatch, you can get the gorgeous Error Boundaries. But the bad news is that it just can capture the error which is going on in the respond lifecycles(constructor, componentDidCatch, render). Here is a good example. If the child component has a button and then pressing on the button throws an exception, that exception will not be captured inside its parent element. You don’t have to mention that this consists of the events like setTimeout & setInterval or some other occasions.

  1. International Error Handling

For other cases, the above methods do not work correctly; there is another way in which we’re able to utilize the global handler to catch the uncaught exceptions.

6.Others

There are a lot of other methods to capture logs like below

  • react-transform-catch-errors
  • react-guard
  • react-component-errors
  • react-safe-render

React 16 (Super Dad)

I call React 16 as Super Dad because what it does to child components.

Error handling in React Native 5

componentDidCatch() a new life cycle method does the above same thing to its child component.

The componentDidCatch() method works like a JavaScript catch {} block, but for components. Only class components can be error boundaries. In practice, most of the time you’ll want to declare an error boundary component once and use it throughout your application.

A class component becomes an error boundary if it defines a new life cycle method called componentDidCatch(error, info)

class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}

componentDidCatch(error, info) {
// Display fallback UI
this.setState({ hasError: true });
// You can also log the error to an error reporting service
logErrorToMyService(error, info);
}

render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}

Then you can use it as a regular component:

<ErrorBoundary>
<MyWidget />
</ErrorBoundary>

Where to use ErrorBoundary ?

  • Root Component (must)
  • Main Sections of the page like Header, Footer and Middle Content
  • Stand-alone components like Chat Window, Search Bar
  • Wherever you suspect that the error might occur

The granularity of error boundaries is up to you. You can wrap top-level route components, you might also wrap components that might contain obscure code. It’s up to you how to best handle protecting against application crashes.

Note that error boundaries only catch errors in the components below them in the tree. An error boundary can’t catch an error within itself. If an error boundary fails when trying to render the error message, the error will propagate to the closest error boundary above it. This, too, is similar to how catch {} block works in JavaScript.

Let me show you some examples of how error boundary works

Error handling in React Native 6

In the above example, there is one <Parent />component which has a <Child />. <Child/> has three sub childrens <Child1/>,<Child2/> and <Child3/>.

<Child3> is throwing some error, which will be thrown to its successive parent <Child>, as the <Child/> does not implement componentDidCatch() it will be thrown to its successive parent which is <Parent/>

Error handling in React Native 7

Output:

Error handling in React Native 8

And console logs this

Error handling in React Native 9

If the componentDidCatch() would have implemented in the <Child/> it would have been more granular

Error handling in React Native 10

Output:

Error handling in React Native 11

When does error boundary catch errors?

  • When there is an error in the render
  • In lifecycle methods
  • In Constructor
  • The whole tree below them

When will  it won’t catch?

  • Errors in the same component
  • On event handlers, unless the error is due to a state change
  • Error in try catch block unless an error is due to state change
  • Error in asynchronous code unless the error is due to state change
  • Server-side rendering

Capturing errors

Error monitoring and crash reporting is a vital part of building a high-quality customer centric application.

A release isn’t the end… Errors are your first chance to iterate.

There are various tools for capturing errors

  1. Sentry
  2. Bugsnag
  3. Firebase
  4. Visual Studio App Center

One of the most popular crash reporting tools for mobile applications is Firebase Crashlytics. But Crashlytics cannot be used in React Native apps because it won’t capture javascript errors. There are other very good crash reporting tools for React Native like Sentry, Bugsnag etc.

One of the most important features of Bugsnag which made the developers life easy was tagging users who got crashes. When a user reports a crash in our app, we can easily search their email id and search for crashes for that particular users.

Conclusion

Building an error-free application is a vital part of product development. React 16 helps us in making React web applications and React Native apps to capture errors and give enough insights to rectify those errors. Especially, here at Groww we use React 16 and using some advanced tools we manage to  avoid most of the errors and give a better user experience to our  end users.

Written by John Francis

John is one of the founding members of Groww (groww.in) and leads front-end technologies. Prior to Groww, he has worked with Flipkart. He is passionate about building delightful user experiences using latest technologies.

10 cool things to check out at Microsoft Build 2019 12

10 cool things to check out at Microsoft Build 2019

Healthcare IoT, analytics, and mobile engagement: An optimistic panorama 13

Healthcare IoT, analytics, and mobile engagement: An optimistic panorama