React Error Boundaries / Application Error Handling

Musema Hassen
2 min readFeb 8, 2020

--

When error occurs in your react app you want to show your users a friendly message. Specially if you are developing a reusable component that will be used in another apps, you don’t want the error to propagate to the parent app, which may break the entire app.

So how do you handle error that may occur in your component gracefully?

Since react follows declarative paradigm we can’t use imperative try {} catch(){} approach. Declarative vs Imperative Programming

React introduces Error Boundaries to solve this issue. The idea is defining error boundary component which we can use as a wrapper to our app so that when error occurs inside our app react will fires lifecycle methods(getDerivedStateFromError and componentDidCatch) defined in error boundary component where we can put our logic to choose what to be displayed(rendered).

React introduces Error Boundaries to solve this issue. The idea is defining error boundary component which we can use as a wrapper to our app so that when error occurs inside our app react will fires lifecycle methods(getDerivedStateFromError and componentDidCatch) defined in error boundary component where we can put our logic to choose what to be displayed(rendered).

We are going to use 4 components to demo this.

App: Simple react component which will contain our app logic which eventually throw exception

AppFallbackUI: A component to display if error occurred.

AppErrorBoundary: A react Error boundary component which will define two lifecycle methods getDerivedStateFromError and componentDidCatch. It will detect any error occurred inside its child components and render appropriate component, App or AppFallbackUI component

AppWithErrorBoundary: Wrapper component which wraps App component with AppErrorBoundary so that any error inside App with be detected.

// App.js 
import React from “react”;
const App = () => {const person = null;return
<div style={{ margin: “50px”, background: “lightgrey”}}>
<h1>Person App</h1>
{person.name} {/* accessing null object, person is null*/}
</div>
}
export default App;// AppFallbackUI.js
// This will be rendered when error thrown
import React from “react”;export const AppFallbackUI = () => { return
<div style={{ margin: “50px”, background: “#FFBABA”}}>
<h3>Sorry,w're experiencing technical difficulties.</h3> </div>
}
// AppErrorBoundary.js
// React error boundary where error lifecycle methods defined
import React from ‘react’;
import { AppFallbackUI } from “./AppFallbackUI”;
import { logger } from “../services/logger”;
export class AppErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { errorFound: false };
}
static getDerivedStateFromError(error) {
// see the render to see how this affects what to be rendered.
return { errorFound: true };
}
componentDidCatch(error) {
logger.logError(error); // simple logger imported above
}
render() { if (this.state.errorFound) {
return <AppFallbackUI /> {/* error detected */}
}
else {
return this.props.children; {/* No error flow */}
}
}}export default AppErrorBoundary;// AppWithErrorBoundary.js
// Wrapper to App component, this can be HOC but simplicity
import React from ‘react’;
import AppErrorBoundary from ‘./AppErrorBoundary’;
import App from ‘./App’;
export const AppWithErrorBoundary = () => <AppErrorBoundary> <App/> </AppErrorBoundary>// index.js
// finally in index.js
import React from ‘react’;
import ReactDOM from ‘react-dom’;
import {AppWithErrorBoundary } from ‘./components/AppWithErrorBoundary’;
ReactDOM.render(<AppWithErrorBoundary />, document.getElementById(‘root’));

--

--