Hello readerš In this blog post, we will give an overview on why catching and handling errors is important in software engineering in general and the ways offered by popular web development tools such as React to effectively handle errors. Why errors happens in software engineering The majority of the well known applications we use on a daily basis, like Facebook, YouTube, etc., are extremely huge, sophisticated and very often made by several people (can be more than 100 engineers for very big products). Very often, despite having many automated systems that are checking for errors before they reach production, there are very often errors in the code of those products that makes it crash for some specific situations. It's not that the developers who created those products are stupid, lazy or incompetent, it's just that, in the rush to meet a deadline, itās difficult to foresee everything a user could do to the system they are working on which often leads to the software crashing in some use cases. Because of the importance of software programs in our current world and the criticality of the areas in which some programs are used such as the healthcare industry, it is important to effectively handle potential errors that might be in the code of those programs in order to offer the best possible product to the end users and to avoid any bad consequence of a faulty program. Why is error handling important in React apps? It is important to handle potential errors that can be in your React app because those errors might end-up crashing the entire React lifecycle or reaching the top-level of the main execution thread. From React 16 onward, as stated in , errors that are not caught will result in unmounting of the whole React component tree. the official documentation Because of the importance of handling errors we stated above, it is very important to handle potential issues that may happen, and eventually provide suitable feedback to the users and also to the developers so that those errors are quickly fixed. Fortunately, implementing such patterns is simple with the recent React APIs. More specifically, React 16 introduces the Error Boundaries mechanism that is very useful for that purpose. Component stack trace From React 16, all errors that occurred during the rendering phase are printed into the console in development. In addition to that, the component stack trace is also provided. We can therefore see where exactly in the component tree the error has happened: The component stack trace additionally includes filenames and line numbers that you can use to see where exactly the error happened. The component stack trace works by default in projects created using . If you donāt use it, you can manually add to your Babel setup. Create React App this plugin How to effectively handle errors in React components ? Catching error with try ⦠catch If you are familiar with JavaScript, you are probably familiar with the usual block used to catch errors. try ⦠catch Using the usual statement is not effective to catch errors in your react components because it only works for imperative code and not the declarative code we write in JSX. try...catch Moreover, with a statement, we might cause the entire react app to break instead of just the faulty component which is not what you most probably want. try...catch Error boundaries Error Boundaries are the recommended way to handle potential errors in React components. According to the , āError boundaries are React components thatĀ catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UIĀ instead of the component tree that crashed. Error boundaries catch errors during rendering, in lifecycle methods, and in constructors of the whole tree below them.ā React documentation Not all errors will be catched with Error boundaries. Below is a list of errors that wonāt be catched using this mechanism: Event handlers Asynchronous code (e.g. or callbacks) setTimeout requestAnimationFrame Server side rendering Errors thrown in the error boundary component itself. Errors Boundaries only catch errors in the components below them in the tree Why Errors Boundaries are useful When using Error Boundaries, errors that might happen in a specific component wonāt make the global component tree to crash. Instead, the error propagation will stop at the Error Boundary level How to implement Error boundaries Error Boundaries are implemented using class components. According to the , a class component becomes an error boundary if it defines either (or both) one of the lifecycle methods or . React documentation static getDerivedStateFromError componentDidCatch is used to render a visual fallback UI after an error has been thrown. It will be called after a descendant component throws an error. This method takes the error as an argument and the value returned by this function is used to update the state. static getDerivedStateFromError(error) is usually used to log error information. It will be called as soon as an error reaches our component. This method accepts two arguments: componentDidCatch(error, info) - The error that was thrown error - An object storing the componentStack trace showing which component has thrown this error. info Simple implementation of error boundaries As we previously mentioned, an error boundary is just a class component that implements one of those methods or or both. static getDerivedStateFromError() componentDidCatch() Real example Here is a simple React app where we are explicitly throwing an error in the Users component below import { Users } from "./Users"; function App() { return ( <div className="App"> <h1>Hello dear reader</h1> <Users /> </div> ); } export default App; Here is the Users component where we are explicitly throwing an error export const Users = () => { throw new Error("Error!"); return <div> Users </div>; }; Running this React app will throw the following error and cause the entire app to crash To prevent the app from entirely crashing, we need to add the Error Boundary component. Here is a simple implementation of an error boundary class import React from "react"; export class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false }; } static getDerivedStateFromError(error) { return { hasError: true }; } componentDidCatch(error, errorInfo) { console.error(error, errorInfo); } render() { if (this.state.hasError) { return <h1>Something went wrong.</h1>; } return this.props.children; } } After defining this, we only need to wrap the necessary component where we might have an error inside the Error Boundary component. import { ErrorBoundary } from "./ErrorBoundary"; function App() { return ( <div className="App"> <h1>Hello</h1> <ErrorBoundary> <Users /> <ErrorBoundary /> </div> ); } export default App; The app now runs but the error propagation stops at the Error Boundary component and the app doesnāt entirely crash. Advanced error handling experience : react-boundary-library If you are not satisfied with the features offered by the Error Boundary component, you can use the that provides a retry mechanism as well as a way to render a fallback component in case of an error. react-boundary-library You can install this library simply by # if you use npm npm install --save react-boundary-library # if you use yarn yarn add react-boundary-library How to use react-boundary-library Firstly, all the features offered by this library are well explained in their . Weāll see here a simple example to help you get started. official repository To handle errors using this library, you can do it simply by wrapping the component where your error might show up inside the component from . You can also provide a fallback component as a prop to the Error Boundary component which will be the UI shown in case of an error. Here is a concrete example ErrorBoundary react-boundary-library import { ErrorBoundary } from "react-error-boundary"; import { Users } from "./Users"; function ErrorFallback({ error, resetErrorBoundary }) { return ( <div role="alert"> <p>Failed to load users:</p> <p>{error.message}</p> <button onClick={resetErrorBoundary}>Try again</button> </div> ); } function App() { return ( <div className="App"> <h1>Hello dear reader</h1> <ErrorBoundary FallbackComponent={ErrorFallback}> <Users /> </ErrorBoundary> </div> ); } The Users component is the same one as before. The Error Boundary component takes one mandatory prop which is the fallback component or JSX code to display in the case of an error. The fallback component accepts and as props. The prop will reset the error boundary's state when called, and can be useful for a "try again" button. is the thrown error. error resetErrorBoundary resetErrorBoundary error Here is the output we get when running the above code Final words Generally speaking, a software error is essentially a mismatch between what is expected from the program and the real output. It is almost impossible to make software that is error-free, and that even with the development of automated testing tools. On some occasions, these errors have little effect, while in other contexts, like in the healthcare industry or in banking applications, the effects of an error can be extremely expensive to fix. It is therefore important to handle any potential error in the most efficient way possible. In the case of web applications and more specifically in the case of web components, modern JavaScript frameworks usually offer ways to handle errors that might be in your web component other than just the usual . Error Boundaries components are an effective solution offered in the React ecosystem for that purpose. This solution helps in offering visual UI feedback and stops the error propagation in order to not cause the entire app to crash. There are as well other libraries that one can use to provide an advanced error-handling experience such as in the React ecosystem. try ⦠catch react-boundary-library If this post was helpful to you in any way, please don't hesitate to share it. You can also follow if you would like to see more content related to web programming. my Twitter account