React Storefront
|

Error Handling

This guide covers how to handle errors in your React Storefront application.

Reporting Errors to an Error Logging Service

Whenever an error is thrown on the client or on the server, src/errorReporter.js is called. Here you can pass the error along to the error logging service of your choice:

// src/errorReporter.js

/**
 * Called whenever an error is throw, including:
 *
 * React rendering on the client and server
 * Route handlers on the server client and server
 * All unhandled rejections on the client
 * All uncaught errors on the client
 *
 * @param {Object} event
 * @param {Error} event.error The error that was thrown.
 * @param {AppModel} options.app The current app state.  This may be null in some cases if the error prevented a state from being computed
 * @param {History} options.history The JS history object.  You can use this to get the current location.
 */
export default function errorReporter({ error, app, history }) {
  // TODO: report errors to the error logging service of your choice here.
  console.error(error)
}

Example: Airbrake

The react-storefront-extensions commercial package makes it easy to report errors to Airbrake:

// src/errorReporter.js

import AirbrakeClient from 'airbrake-js'
import { createAirbrakeReporter } from 'react-storefront-extensions/error-reporters'

export default createAirbrakeReporter(
  new AirbrakeClient({
    projectId: XXXXXX,
    projectKey: 'XXXXXXXXXXXXXXXXXXXXXXX'
  })
)

Handling Uncaught Errors

React Storefront's Router provides an error handler that is called whenever an error is thrown from a route handler on either the client or the server:

new Router().error((error, params, request, response) => {
  response.status(500)

  return {
    page: 'Error',
    error: e.message,
    loading: false,
    stack: e.stack
  }
})

Like all other route handlers, the error handler returns data to be applied to the app's state tree. In the example above, we return a state object that will cause the app to hide the loading mask (if one is present) and display the error page. This matches the default functionality that is provided if you do not provide an error handler.

Errors That Occur During React Rendering

React Storefront automatically catches all errors that occur during React rendering and updates the following attributes on the app state:

  • page is set to "Error"
  • error is set to the error's message
  • stack is set to the error's stack

The Error Page

You can configure a catch-all error page for your app in the Pages component in src/App.js.

<Pages
  loadMasks={{
    Category: CategorySkeleton,
    Subcategory: SubcategorySkeleton,
    Product: ProductSkeleton
  }}
  components={universal => ({
    Home: universal(import('./home/Home')),
    Category: universal(import('./category/Category')),
    Subcategory: universal(import('./subcategory/Subcategory')),
    Product: universal(import('./product/Product')),
    Cart: universal(import('./cart/Cart')),
    Checkout: universal(import('./checkout/Checkout')),

    // Here we configure the page to displayed when an error occurs
    Error: universal(import('./ErrorPage')),

    Offline
  })}
/>

When you create a new app with create-react-storefront, it comes with a default error page that displays the full stack trace in development, and a simple error message in production:

import React, { Component } from 'react'
import Typography from '@material-ui/core/Typography'
import Container from 'react-storefront/Container'
import Row from 'react-storefront/Row'
import Redbox from 'react-storefront/Redbox'

export default class ErrorPage extends Component {
  render() {
    if (process.env.MOOV_ENV === 'production') {
      // In production we return a generic, user-friendlt error page that hides the underlying message and stacktrack
      return (
        <Container>
          <Row>
            <Typography variant="h6">Error</Typography>
          </Row>
          <Row>
            <Typography>
              An unknown error occurred while attempting to process your request. Please try again
              later.
            </Typography>
          </Row>
        </Container>
      )
    } else {
      // In development, we display react-storefront/Redbox to help with debugging
      return <Redbox />
    }
  }
}