Encapsulate a simplest ErrorBoundary component to handle react exceptions

Encapsulate a simplest ErrorBoundary component to handle react exceptions

Preface

Starting from React 16, the concept of Error Boundaries was introduced, which can capture errors generated in its child components, record error logs, and display downgrade content. The specific official website address

Error boundaries prevent a component error from causing the entire page to become unusable with a blank screen. They use graceful degradation to present an alternative UI. Error boundaries can capture errors during rendering, in the lifecycle, and in the constructor of the entire component tree. Since React 16, any error not caught by an error boundary will cause the entire React component tree to be unmounted.

ErrorBoundary meaning

  • Some UI crashes, but not the entire webapp crashes

When browsing a page, the user experience may be poor due to exceptions returned by the backend or some error checks in the front end. Just imagine that you are sitting on a train with your wife, eating hot pot and singing songs, and suddenly you are robbed by bandits and an error message appears. In some scenarios, such as when setting the amount or viewing key pages, the experience will be very bad. For example, you recharged 500 in the game, but the recharge NaN is displayed due to interface reasons. This display is more annoying than no display. However, I believe everyone is familiar with JS exception capture, and a try-catch package of business code is enough. However, to capture exceptions in components, you need to use the Error Boundary feature provided by React, and use componentDidCatch hook to capture page exceptions so that the exceptions will not spread to the entire page, effectively preventing the page from displaying a blank screen.

How to implement the official website

👉 If a class component defines any (or both) of the two lifecycle methods static getDerivedStateFromError() or componentDidCatch(), it becomes an error boundary. When an error is thrown, use static getDerivedStateFromError() to render the alternate UI and componentDidCatch() to print the error message 👈

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }
  static getDerivedStateFromError(error) {
    // Update state so that the next rendering can display the degraded UI
    return { hasError: true };
  }
  componentDidCatch(error, errorInfo) {
    // You can also report error logs to the server logErrorToMyService(error, errorInfo);
  }
  render() {
    if (this.state.hasError) {
      // You can customize the downgraded UI and render return <h1>Something went wrong.</h1>;
    }
    return this.props.children;
  }
}

Then you can use it as a regular component:

<ErrorBoundary>
  <MyWidget />
</ErrorBoundary>

Error boundaries work similarly catch {} in JavaScript, except that they only apply to React components. Only class components can become error boundary components. In most cases, you only need to declare an error boundary component once and use it throughout your application. When using it, errors in the wrapped component or exceptions thrown by throw new Error() can be caught by the error boundary component and displayed as a fallback UI.

Encapsulates a configurable ErrorBoundary

After understanding how the official website implements the error boundary component, we can encapsulate an ErrorBoundary component to create a useful wheel instead of directly writing return <h1>Something went wrong</h1> . After learning the principles of react-redux , we know that we can use high-order components to wrap react components and inject data and methods in store globally. Similarly, we can also use high-order components to wrap it into a react component that can capture errors.

1. Create a configurable ErrorBoundary class component

Compared with ErrorBoundary on the official website, we can dynamically configure the log reporting method and the displayed UI by accepting parameters. For the incoming UI , we can set it to be accepted as react component or a React Element . And through the component, we can pass in parameters, so that we can get specific error information in the underlying UI.

  • componentDidCatch(): hook function for error log processing
  • static getDerivedStateFromError() : It takes the thrown error as a parameter and returns a value to update the state
class ErrorBoundary extends React.Component {
  state = { error: false };
  static getDerivedStateFromError(error) {
    return { error };
  }
  componentDidCatch(error, errorInfo) {
    if (this.props.onError) {
      //The report log is executed through the function injected by the parent component this.props.onError(error, errorInfo.componentStack);
    }
  }
  render() {
    const { fallback, FallbackComponent } = this.props;
    const { error } = this.state;
    if (error) {
      const fallbackProps = { error };
      //Determine whether it is a React Element
      if (React.isValidElement(fallback)) {
        return fallback;
      }
      //Component method if (FallbackComponent) {
        return <FallbackComponent {...fallbackProps} />;
      }
      throw new Error("ErrorBoundary component needs to be passed into the fallback UI");
    }
    return this.props.children;
  }
}

In this way, the underlying UI display and錯誤日志can be dynamically obtained, making the component more flexible. However, there is another problem. Sometimes, the server suddenly fails with 503 or 502, and the front end cannot get a response. At this time, a component reports an error, but it returns to normal after a while. A better approach is for the user to click a method in the component encapsulated by ErrorBoundary to reload the error component without refreshing the page. At this time, the component that needs to be covered should expose a method for ErrorBoundary to handle.

1. Add a method in ErrorBoundary to detect whether there is an injected reset method. If there is a reset method, execute it and reset the error in state to make its error state false

resetErrorBoundary = () => {
  if (this.props.onReset) this.props.onReset();
  this.setState({ error: false });
};

2. Add a function component type in render for rendering. The reset method and error information can be passed as parameters to the current component for processing.

 render() {
    const { fallback, FallbackComponent, fallbackRender } = this.props;
    const { error } = this.state;
    if (error) {
      const fallbackProps = {
        error,
        resetErrorBoundary: this.resetErrorBoundary,
      };
      ...
      if (typeof fallbackRender === "function")return fallbackRender(fallbackProps);
      ...
    }
    return this.props.children;
  }

2. Wrap ErrorBoundary with a high-order function and return it

import React from "react";
import DefaultErrorBoundary from "./core";
const catchreacterror = (Boundary = DefaultErrorBoundary) => InnerComponent => {
  return props => (
    <Boundary {...props}>
      <InnerComponent {...props} />
    </Boundary>
  );
};

Usage & Testing

Through a click-to-increment demo, when the number reaches a certain value, an exception is thrown. Here, the class component and the Function component are tested as the components that initiate the exception.

  • The component that caused the exception
//Function component const fnCount1 = ({ count }) => {
  if (count == 3) throw new Error("count is three");
  return <span>{count}</span>;
};
//Class component class fnCount2 extends React.Component {
  render() {
    const { count } = this.props;
    if (count == 2) throw new Error("count is two");
    return <span>{count}</span>;
  }
}
  • Function component for handling error exceptions
const errorbackfn = ({ error: { message }, resetErrorBoundary }) => (
  <div>
    <p>Something went wrong</p>
    <pre>{message}</pre>
    <button onClick={resetErrorBoundary}>Try again</button>
  </div>
);
  • Common components for handling error exceptions
const errorbackcom = () => <h1>An error has occurred and cannot be undone</h1>;
  • Test Components
//Wrap the component that initiated the exception and return a high-order component that can handle error editing const SafeCount1 = catchreacterror()(fnCount1);
const SafeCount2 = catchreacterror()(fnCount2);
//Test main component const App = () => {
  const [count, setCount] = useState(0);
  const ListenError = (arg, info) => console.log("Error: " + arg.message, info); //Callback when error occurs const onReset = () => setCount(0); //Callback when reset is clicked return (
    <div className="App">
      <section>
        <button onClick={() => setCount(count => count + 1)}>+</button>
        <button onClick={() => setCount(count => count - 1)}>-</button>
      </section>
      <hr />
      <div>
        Class component:
        <SafeCount2
          count={count}
          fallbackRender={errorbackfn}
          onReset={onReset}
          onError={ListenError}
        />
      </div>
      <div>
        Function component:
        <SafeCount1
          count={count}
          FallbackComponent={errorbackcom}
          onError={ListenError}
        />
      </div>
    </div>
  );
}; 

Mission accomplished!

Problems encountered & summary

There are many times when react error boundaries are not omnipotent, such as

  • Event Error

In the above example, this.o does not exist, so an error will be reported. Window.onerror can capture it, but the error boundary cannot.

  • Asynchronous code

Server-side rendering and error boundaries themselves

Summarize

  • Extract components✔
  • Bug Report✔
  • UI abstraction ✔
  • Error reset ✔
  • Unhook mode ✖
  • Server ✖

So far, thank you for taking the time to read this article. I hope it can be helpful to you. I believe that you have a general understanding of the error boundaries in react and can write a simple ErrorBoundary In general, there are still many points for optimization. If you have any questions, you are welcome to correct me. For more information about ErrorBoundary and react, please pay attention to other related articles on 123WORDPRESS.COM. We hope that everyone will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • Implementation of Portals and Error Boundary Handling in React
  • React error boundary component processing
  • Detailed explanation of exception handling in React 16
  • React workflow and Error Boundaries implementation process explanation

<<:  MySQL 8.0.3 RC is about to be released. Let’s take a look at the changes

>>:  How to query and update the same table in MySQL database at the same time

Recommend

Example code for implementing WeChat account splitting with Nodejs

The company's business scenario requires the ...

Detailed graphic tutorial on installing Ubuntu 20.04 dual system on Windows 10

win10 + Ubuntu 20.04 LTS dual system installation...

Summary of MySQL commonly used type conversion functions (recommended)

1. Concat function. Commonly used connection stri...

How to update, package, and upload Docker containers to Alibaba Cloud

This time, we will try to package the running con...

Use of marker tags in CSS list model

This article mainly introduces the ::master pseud...

Implementation of React star rating component

The requirement is to pass in the rating data for...

Causes and solutions for MySQL data loss

Table of contents Preface Problem Description Cau...

Detailed explanation of InnoDB storage files in MySQL

Physically speaking, an InnoDB table consists of ...

How does JS understand data URLs?

Table of contents Overview Getting started with d...

Vue implements fuzzy query-Mysql database data

Table of contents 1. Demand 2. Implementation 3. ...

In-depth understanding of the use of Vue

Table of contents Understand the core concept of ...

Some key points of website visual design

From handicraft design to graphic design to web de...

Summarize some general principles of web design and production

<br />Related articles: 9 practical suggesti...