DEV Community

Cover image for Handle errors in React components like a pro
Edem Agbenyo
Edem Agbenyo

Posted on

Handle errors in React components like a pro

When you are writing a react application you have two ways to handling errors:

  • Using try/catch block in each component
  • Using React Error Boundary which is only available in class Component :(
import * as React from 'react'
import ReactDOM from 'react-dom'

function City({name}) {
  return <div>Hello, visit {name.toUpperCase()}</div>
}

function Country({capital}) {
  return <div>Hello, visit {capital.toUpperCase()}</div>
}

function App() {
  return (
    <div>
      <Country />
      <City />
    </div>
  )
}

ReactDOM.render(<App />, document.getElementById('root'))
Enter fullscreen mode Exit fullscreen mode

The above piece of code would end up showing you an error page when you run it in Development or a blank screen in Production.
Obviously, the error we created in the code above could have certainly been handled with PropTypes or TypeScript, however we are aware runtime error happens all the time and we are going to deal with it using the two approaches stated above.

Try/catch

import * as React from 'react'
import ReactDOM from 'react-dom'

function ErrorHandler({error}) {
  return (
    <div role="alert">
      <p>An error occurred:</p>
      <pre>{error.message}</pre>
    </div>
  )
}

function City({name}) {
  try {
    return <div>Hello, visit {name.toUpperCase()}</div>
  } catch (error) {
    return <ErrorHandler error={error} />
  }
}

function Country({capital}) {
  try {
    return <div>Hello, visit {capital.toUpperCase()}</div>
  } catch (error) {
    return <ErrorHandler error={error} />
  }
}

function App() {
  return (
    <div>
      <Country />
      <City />
    </div>
  )
}

ReactDOM.render(<App />, document.getElementById('root'))

Enter fullscreen mode Exit fullscreen mode

This approach, requires us to define an ErrorHandler component to display in case an error occurs and we wrap each component returned element in the try/catch block.

This seems ok, but repetitive. What if we want the parent component to handle the error catching for us. Wrapping the parent component App in a try/catch block will not work, due to the nature of how React calls functions. That is when React Error Boundary comes in.

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

As at React 17.0.2, Error Boundary works only in

  • Class component
  • and It must implement static getDerivedStateFromError() or componentDidCatch()

In order to use Error Boundary in Functional Component, I use react-error-boundary.

import * as React from 'react'
import ReactDOM from 'react-dom'
import {ErrorBoundary} from 'react-error-boundary'


function ErrorHandler({error}) {
  return (
    <div role="alert">
      <p>An error occurred:</p>
      <pre>{error.message}</pre>
    </div>
  )
}

function City({name}) {
    return <div>Hello, visit {name.toUpperCase()}</div>
}

function Country({capital}) {
    return <div>Hello, visit {capital.toUpperCase()}</div>
}

function App() {
  return (
    <ErrorBoundary FallbackComponent={ErrorHandler}>
      <Country />
      <City />
    </ErrorBoundary>
  )
}

ReactDOM.render(<App />, document.getElementById('root'))
Enter fullscreen mode Exit fullscreen mode

When we run this application, we will get a nice error display form the content of ErrorHandler component.
React error boundary catches any error from the components below them in the tree. This is really handy and useful because we need not declare a separate try/catch for each component because the wrapping component(ErrorBoundary) takes care of that and display the component of the FallbackComponent provided.

Exceptions to Error handling

Because react-error-boundary uses react error boundary in the background there are a few exceptions to the errors that can be handled.

These errors are not handled by react-error-boundary

  • Event handlers
  • Asynchronous code (e.g. setTimeout or requestAnimationFrame callbacks)
  • Server side rendering
  • Errors thrown in the error boundary itself (rather than its children)

Error recovery

This library offers an error recovery feature, that allow you to reset the state and bring back the components to a working point.
Let's use this example from the react-error-boundary npmjs page.

function ErrorFallback({error, resetErrorBoundary}) {
  return (
    <div role="alert">
      <p>Something went wrong:</p>
      <pre>{error.message}</pre>
      <button onClick={resetErrorBoundary}>Try again</button>
    </div>
  )
}

function Bomb() {
  throw new Error('💥 CABOOM 💥')
}

function App() {
  const [explode, setExplode] = React.useState(false)
  return (
    <div>
      <button onClick={() => setExplode(e => !e)}>toggle explode</button>
      <ErrorBoundary
        FallbackComponent={ErrorFallback}
        onReset={() => setExplode(false)}
        resetKeys={[explode]}
      >
        {explode ? <Bomb /> : null}
      </ErrorBoundary>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

The ErrorBoundary component accepts two other props to help recover from a state of error. The first prop onReset receives a function which will be triggered when resetErrorBoundary of the FallbackComponent is called. The onReset function is used to reset the state and perform any cleanup that will bring the component to a working state.
The other prop of ErrorBoundary is resetKeys, it accepts an array of elements that will be checked when an error has been caught. In case any of these elements changes, the ErrorBoundary will reset the state and re-render the component.

Handling error in React functional components should be a breeze for anyone using the react-error-boundary library. It provides the following features:

  • Fallback components to display incase of error
  • Granular capturing of error at component level
  • Recovery of error using a function or by resetting the elements causing the component to fail.

Top comments (3)

Collapse
 
microgold profile image
Mike Gold

Nice, thanks for writing an easy to follow tutorial.

Collapse
 
epicdeveloper0928 profile image
Rascal

Excellent!

Collapse
 
olix profile image
Noibi Abdulrasaq

Brilliant!