cft

Handling errors in react

Error boundaries in React


user

Anthony Muonaka

a year ago | 6 min read

Errors are bound to occur in our applications. They can either be compile time errors, or runtime errors.

And there are many methods that can be used to prevent most of these errors from happening or control the occurrence of such errors.

In react, one common method for handling errors is the use of error boundaries.

What are error boundaries?

Error boundaries are React components that are used to handle JavaScript errors in their child component tree. They were introduced in react v16 to provide some sort of checks or boundaries to errors.
They basically catch and handle JavaScript errors that occur in the UI parts of our component. This means that they can only catch errors that occur during rendering, lifecycle methods or useEffect.

According to the react documentation, error boundaries cannot handle errors in the following events:

  • event handlers
  • asynchronous code (e.g., setTimeout)
  • server-side rendering
  • errors thrown in the error boundary itself (rather than its children)

Error boundaries work like a JavaScript catch {} block, but for components. Only class components can be error boundaries.

In this article, we are going to work on handling errors for both in our react project for both class components and react hooks.
We will create a mechanism, whereby if an error occurs, the user should get a custom message rather than throwing a run time error from the component.

Error boundary in a class component

To set up an error boundary, we need to create a class component
We will see what will happen without adding an error boundary.

import React from "react";

class Counter extends React.Component {

state = {

counter: 0,

};

handleClick = () => {

this.setState({

counter: this.state.counter + 1,

});

};

render() {

if (this.state.counter === 5) {

throw new Error("Unhandled error.");

}

return (

<div>

<h1>{this.state.counter}</h1>

<button onClick={this.handleClick}>+</button>

</div>

);

}

}

export default Counter;

In the example above, when the counter gets to 5, it creates an error and then breaks the UI display. And so the user cannot proceed until the error is fixed.
This should not be the case, an error that could have been handled should not break our code. And that is where error boundary comes in.

Next we are going to write this same code but this time we will add error boundary.
First we create an errorBoundary.js file

import React from "react";

class ErrorBoundary extends React.Component {

state = {

errorMessage: "",

};

static getDerivedStateFromError(error) {

return { errorMessage: error.toString() };

}

render() {

if (this.state.errorMessage) {

return (

<>

<h2>An Error Has Occured</h2>

<details>{this.state.errorMessage}</details>

</>

);

}

return this.props.children;

}

}

export default ErrorBoundary;

We created an errorBoundary that checks if there is an error and displays a customized error when there is an error.

We use the static getDerivedStateFromError life cycle method to check and update the state of the error.

import React from "react";

class Counter extends React.Component {

state = {

counter: 0,

};

handleClick = () => {

this.setState({

counter: this.state.counter + 1,

});

};

render() {

if (this.state.counter === 5) {

throw new Error("Handled error.");

}

return (

<div>

<h1>{this.state.counter}</h1>

<button onClick={this.handleClick}>+</button>

</div>

);

}

}

export default Counter;

Our counter file still remains the same. Then in the App.js

import React from "react";

import Counter from "./components/counter";

import ErrorBoundary from "./components/ErrorBoundary";

function App() {

return (

<>

<ErrorBoundary>

<Counter />

</ErrorBoundary>

</>

);

}

export default App;

Run npm start

We wrapped the errorBoundary component over our counter file. Now any error that occurs in the counter file will be picked up by the errorBoundary and this will prevent the app from failing.

Error handling with react hooks

import React, { useEffect, useState } from 'react'

export default function Counter() {

const [counter, setCounter] = useState(0);

const handleClick = () => {

setCounter(counter + 1)

}

useEffect(() => {

if (counter === 5) {

throw new Error("unHandled error.");

}

}, [counter]);

return (

<>

<h1>{counter}</h1>

<button onClick={handleClick}>+</button>

</>

)

}

Now we need to create a variable "isError" to check if there is an error or not.

export default function Counter() {

const [counter, setCounter] = useState(0);

const [isError, setIsError] = useState(false);

const handleClick = () => {

setCounter(counter + 1)

}

useEffect(() => {

try {

if (counter === 5) {

throw new Error("unHandled error.");

}

} catch {

setIsError(true);

}

}, [counter]);

If there is an error, we set "isError" to true.

{isError ? (

<h1>Caught an error.</h1>

) : (

<div>

<h1>{counter}</h1>

<button onClick={handleClick}>+</button>

</div>

)}

In the code above, we created a ternary operation to check if "isError" is true or false. If "isError" is true, we show an error message. But if "isError" is false, we render the normal component.

Here is the entire code below

import React, { useEffect, useState } from "react";

export default function Counter() {

const [counter, setCounter] = useState(0);

const [isError, setIsError] = useState(false);

const handleClick = () => {

setCounter(counter + 1);

};

useEffect(() => {

try {

if (counter === 5) {

throw new Error("unHandled error.");

}

} catch {

setIsError(true);

}

}, [counter]);

return (

<>

{isError ? (

<h1>Caught an error.</h1>

) : (

<div>

<h1>{counter}</h1>

<button onClick={handleClick}>+</button>

</div>

)}

</>

);

}

React-error-boundary library

Another way to implement error boundary in react is by using the react-error-boundary library. This help us to handle error boundaries without actually building our own code from scratch.

We simply wrap the components where we are likely to expect errors and pass the required props. Here’s an example of how to use the errorBoundary library

npm install --save react-error-boundary

import React from "react";

import { ErrorBoundary } from "react-error-boundary";

function ErrorHandler({ error }) {

return (

<div role="alert">

<p>An error occured</p>

<pre>{error.message}</pre>

<button onClick={resetErrorBoundary}>Try again</button>

</div>

);

}

function App() {

return (

<ErrorBoundary FallbackComponet={ErrorHandler}>

onReset={() => {

// reset the state of your app so that the error doesn't happen again

}}

>

<ComponentThatMightThrowAnError />

</ErrorBoundary>

);

}

What we did in the code above was first to create an errorHandler component then we simply wrap our component inside the Error Boundary component and pass the fallback props so that if there is an error it will be caught and handled by the error boundary component otherwise, it will just render our component.

There are many more functions that you can perform with this library. You can click here to read more React-error-boundary.

Conclusion

In this article, we learned about error boundaries and how to handle errors in React. There are many ways that you can handle your errors in React and it is also advisable to handle our errors because it creates a fallback UI that users can see instead of the blank screens.

Thanks for reading! Do share and give a like if this article has helped you in understanding error handling in React. Cheers! 👋

Upvote


user
Created by

Anthony Muonaka

Anthony is a fullstack software engineer with a primary focus on frontend development and experience building high-visibility, scalable, and accessible web applications.


people
Post

Upvote

Downvote

Comment

Bookmark

Share


Related Articles