When a React component throws a JavaScript error, it tends to crash the whole application. Because of this, it's important to capture that error and display a meaningful feedback message - while also preventing the entire application from collapsing.

This is usually handled via an ErrorBoundary component in React. It’s a component, usually a Higher Order Component (HOC), that catches JavaScript errors in its child component tree and returns a fallback UI instead of crashing the application.

According to React’s own documentation, ErrorBoundaries don't catch errors for:

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

These components are able to catch errors with the help of some lifecycle methods like getDerivedStateFromError and componentDidCatch. At the time of writing, a function component can't become an ErrorBoundary. There are plans however to expose the relevant lifecycle methods in hooks so that a function component can become an ErroBoundary. Until then, it depends on the project’s architecture how ErrorBoundaries are implemented.

In a JSS project that utilises function components a similar strategy can be implemented. Knowing where the most common rendering errors come from and what they are, an HOC can be prepared to wrap all renderings and catch any errors thrown. For example, a missing datasource rendering error as could be the case for a Sitecore CMS solution.

Enjoying this article? Sign up to our newsletter

Setup

For the purposes of catching errors in a React based JSS application, we create an HOC called withFieldsCheck using a common naming convention. To catch rendering errors, they need to be identified:

  • Datasource errors: For components that rely on data coming from a datasource item
  • Route field errors: For components that rely on data being present on the page template itself
  • Placeholder errors: For components whose children rely on datasource errors

First, create withFieldsCheck HOC. This accepts a component to enhance, as well as other options. It also imports a simple Error component to return instead of the wrapped Componentin in the case an error has been caught.

With fields check

Next, set the logic for catching the three possible errors. Use the options passed to withFieldsCheck to determine what type of rendering error this component will be looking for.

Route field
  • if both routeField and placeholderName are missing, the component is watching for a datasource error
  • if routeField is present, the component is watching for an error due to a field missing on the page template
  • if placeholderName is present, the component is watching for a datasource missing in one of its placeholder child components

It's important to note that:

  • routeField is the name of a content field on the page template the component will be placed on; should that field be missing, the component has been placed on a page it may not supposed to be on
  • placeholderName is the name of a placeholder field defined when setting up a component. It presents itself as an array of all children inside the component props which can then be iterated and those children checked for missing datasources

Both option fields routeField and placeholderName are of type string. The conditions are then checked before returning the component in case an error is found.

Component return

When performing the placeholderNamecheck an additional assertion is needed where the component's children are checked for missing datasources.

Field check index

If the relevant condition check returns an error, the Error component is being rendered instead of the expected wrapped Component.

Inside Error/index.js:

Error index

The error component checks whether you're in editing mode. If so, it returns a relevant error block so the content editing team sees that something is wrong with the rendering. Otherwise, it will return null; so that the application doesn’t crash.

The final step is to use withFieldsCheck to wrap the relevant components.

Wrap components

At Codehouse we're always looking at ways to improve the development process. We continually work on exciting projects often on CMS like Sitecore.