snow-mountain

Handling Exceptions in React: Best Practices


Last updated on
ReactWeb Development

Summary (TL;DR): In this article, we are going to learn about the best to handle exceptions in a React application. We’ll explain what exception handling is, why you should handle exceptions, the different methods of handling errors in React, and finally the best practices for doing it.

Errors are a deviation from the normal flow of operation of an application. When errors occur, it creates unwanted events, and if not properly handled, can be problematic and create a bad user experience.

Exception handling refers to methods put in place, to make provision for different kinds of errors that may occur in an application, either informing the user about the error or handling the abnormality, to prevent the application from coming to a stand-still.

In this article, we will learn about the best practices to handle errors that may occur, and how they can best handle exceptions in a React application.

Table of Contents

What Is Exception Handling?
Why Should We Handle Exceptions?
Methods of Handling Errors in React
>> Using a Try-Catch Block
>> Error Boundary in React
Best Practices To Handle Exceptions in React

What Is Exception Handling?

Errors in an application refer to unwanted events which may occur while a program runs. Errors, if left unchecked, can cause deviations in the normal running of the application and could cause results nowhere near what’s expected.

What then is exception handling? exception handling can be defined as recovery procedures, put in place to prevent unwanted events caused by errors when a program runs. These procedures could also contain a set of instructions that a program could execute upon encountering particular sets of errors. The practice of exception handling prevents a program from coming to a standstill as a result of errors that disrupt its running process.

Why Should We Handle Exceptions?

The following are the reasons why you should consider defining exception-handling procedures in your application:

  • Exception handling prevents programs from crashing as they dictate procedures that will be followed in the event of an exception occurring.
  • Prevents the program from returning unwanted results due to unaccounted-for changes in events that may occur during processing. An example of an unaccounted-for event could be incorrect user input. For example, a program could be written to calculate factorials of numbers. Factorials are usually calculated on positive integers, so in the event where a user enters a negative integer(the factorial would be a complex number), decimals would not be calculable
  • The program ought to prompt the user for a correct entry which would be a positive integer, but if no procedure is put in place to cater for such events, the program would run and produce unwanted results.  

Error handling and exception handling are considered to be quite similar and may be used interchangeably, however, they differ in the following aspects:

Errors are problems that occur in applications and are issues caused by inaccurate actions or mistakes in code. Errors are usually captured by a general exception handler and displayed to the developer to fix. Exceptions, on the other hand, are conditions created to detect errors that may occur in the application, whose result will be detrimental to it, with the aim of providing procedures that can handle the issue, provide the needed result and keep the application running. Errors occur from unhandled exceptions.

Methods of Handling Errors in React

Error handling refers to the procedure of anticipating errors that may occur, detecting them, and addressing such errors in order to produce desired results and aid the continuous running of the program. Errors can occur in 3 stages of a program, namely:

  • Logical errors: These are errors that are caused by a mistake of reasoning on the part of the programmer. Since logical errors are semantically correct, they are not flagged by the program as errors, and as such can be detected by the developer running a test, examining the final result of the program, and analyzing the code at different stages of the program.
  • Compilation error: This is a state where the program fails to compile due to errors (usually syntax error/semantic error) that are present in the source code. Examples of events that can trigger compilation errors are: misspelled reserved words, or misuse.
  • Run-time errors: These are errors that occur during the execution of the source code and can be caused by a program crashing or producing a wrong output that it cannot handle. Examples of events that can trigger run-time errors are: dividing by zero and calling undefined functions.

 Let’s look at different methods with which we can detect and handle errors:

Using a Try-Catch Block

Try-catch blocks have been a popular way to detect and handle run-time errors. The Try-catch block is made up of two code blocks:

try {
  //The try block in javascript contains lines of code which might cause an error/exception
} catch (e) {
  //lines of code to be executed when an exception occurs
}

When any part of the code in the try block causes an exception/error, the control is transferred to the catch block, skipping the rest of the code in the try block. The catch block is meant to contain procedures to display the error that occurred and to handle the error to produce the desired result.

Below is an example of the Try-catch block in action:

let answer;
const handleDivision = (x, y) => {
  return x / y;
};
try {
  //This will be executed
  var x = prompt("Enter a number");
  var y = prompt("Enter another number");
  console.log("Division of " + x + " and " + y + " is ");
  answer = handleDivision(x, y);
  console.log(answer);
  if (answer === Infinity) {
    throw new Error("Division by zero is not allowed");
  }
  //If answer === infinity returns true this will not be executed, this will not be executed
  console.log("Answer is not infinity");
} catch (e) {
  console.log(e);
  console.log(e.message);
  console.log(e.description);
  console.log(e.stack);
}

Here, we have a function to divide numbers. We will get user inputs via a prompt. When the user inputs two numbers, a division is carried out. We are throwing an exception when the result of the division is infinity due to division with zero. In our catch block, we can return the error and trace the stack where it occurs.

The throw method is used to throw errors and messages based on particular conditions for an exception. When we divide a number by zero, we get the following result:

Note that the Try-catch block can be extended by adding an additional block; finally which contains the code you want to be executed regardless of if an exception or error occurs. The syntax is as follows:

try{
  //trial code
}catch(){

   //Code for handling exception
}finally{

  //Code here is executed whether there is an exception or not
}

Error Boundary in React

React error boundary is a method to detect errors that occur within a particular component, log the occurring error, and display a fallback component when errors occur instead of displaying the component which had an error with its crash tree error log. To make use of the error boundary, we will be using an npm package react-error-boundary.

import {ErrorBoundary} from ‘react-error-boundary’

For this to work, we will need to follow the following steps: First, we will create a fallback component to be displayed in the event of an error, then we wrap components in which error may take place, within the error boundary:

function myFallback({ error }) {
  return (
    <div>
      <p>An error occurred:</p>
      <pre>{error.message}</pre>
    </div>
  );
}

function App() {
  return (
    <ErrorBoundary FallbackComponent={ErrorHandler}>
      //components to be covered by the error boundary
    </ErrorBoundary>
  );
}

In the event of an error occurring, the myFallback component will be displayed and will output an error message.

Best Practices To Handle Exceptions in React

When handling exceptions in a React application, below are practices you should consider in order to maximize performance and effectively handle exceptions:

  • Normalize creating and using error boundaries in your application as it’s an effective way to detect errors within a component, show a fallback component with the error details, and reset the state to prevent the same issue from re-occurring.
  • Do not overuse Try-catch blocks. Try-catch blocks do not detect errors that occur within child components. To properly handle such errors, we use error boundaries instead. Only Try-catch code that could cause issues and exception handling is key.
  • Do not use browser-specific syntax for handling exceptions, else you will need a new exception within it to detect other browsers. Ideally, exceptions within an exception will be unwanted practice, as such, it is best practice to properly define exceptions for all browser support.
  • When running asynchronous functions, if you wish to use the Try-catch method for handling exceptions, note that since its an asynchronous function in the Try block, even if the promise is not later resolved the Catch block won’t be executed as the Try-catch block does not wait for the promise:
async function fetchDataFromAPI() {
  // fetches data
}

try {
  //here we check if the promise is resolved else we throw an error
  fetchDataFromAPI();
} catch (err) {
  // This catch block is not reached
}

To fix this, when using the Try-catch  block with asynchronous functions, always use the await keyword to await the promised fulfillment and transfer control to the Catch block if the asynchronous function in the Try block returns an error:

async function fetchDataFromAPI()
{
 // fetches data
}

try
{
  //here we check if the promise is resolved else we throw an error 
  await fetchDataFromAPI()
}
catch(err)
{
  // This catch block is will be reached
}
  • Creating independent components, which make up the general application, will make it easier to trace where exceptions occur and handle them promptly as compared to a monolithic application.

Conclusion

We have come to the end of this guide. In this article, we learned what exceptions are in an application, their causes, and their resultant effects when left unattended. We also cover best practices for handling exceptions to ensure the smooth running of a React application.