In the past, code exceptions within components would cause React’s internal state to be corrupted, producing errors that might not be traceable. But React doesn’t provide a way to gracefully handle or recover from these errors.

By default, a component error during render will result in the entire component tree being unloaded, which is certainly not the desired result.

The failure of some components should not cause the entire application to crash. React 16 addresses this problem by introducing a new conceptError boundary

The error boundary is a React componentYou can catch exceptions that occur anywhere in the child component tree and print those errors while displaying the degraded UIAnd does not render the subcomponent tree where the crash occurred.

Note:

  1. Error boundaries are currently only available inClass ComponentIn, but not inhooks(because Error Boundaries implement this. SetState can pass callbacks, useState can’t pass callbacks, so can’t completely check);
  2. Error boundaryUnable to captureErrors occur in the following four scenarios:
    • Event handlers (because Error Boundaries are essentially implemented to trigger updates, but event handlers are not in the Render or commit phase and cannot be captured, you can use native if you need to catch errors inside event handlerstry / catchstatementsTo learn more)
    • Asynchronous code (e.gsetTimeout 或 requestAnimationFrameCallback function)
    • Server rendering (because triggering updates can only be done on the client side, not on the serve side)
    • The Error it throws (because the Error throws bubble up to the parent and Error Boundaries are not handled)

Implementation:

React provides two error-handling apis:

  • getderivedstatefromerrorStatic method that provides an opportunity to render the Fallback UI when an error occurs
  • componentDidCatch: component instance method that provides an opportunity to log error messages when errors occur

If either (or both) of the life cycle methods getDerivedStateFromError() or componentDidCatch() is defined in a class component, it becomes an error boundary. When an error is thrown, render the alternate UI using getDerivedStateFromError() and print the error message using componentDidCatch().

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      hasError: false}; }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
    logErrorToService(error, errorInfo);
  }
  render() {
    if (this.state.hasError) {
      // You can customize the degraded UI and render it
      return <h1>wrong message</h1>;
    }
    return this.props.children; }}export default ErrorBoundary;
Copy the code

You can then use it as a regular component:

<ErrorBoundary>
  <A />
  <B />
  <C />
</ErrorBoundary>
Copy the code

The error boundary works like the native catch {}, except that it only works for React components. And only class components can be error bound components. In most cases, you only need to declare the error boundary component once and use it throughout the application.

Where should the error boundary be placed?

The granularity of the error boundary is up to you, and you can wrap it in the topmost routing component and present the user with a “XXX” error message, just as server-side frameworks often handle crashes. You can also wrap individual components around error boundaries to protect other components from crashing. (For example, Facebook Messenger wraps the sidebar, message panel, chat history, and message entry fields in separate error boundaries. If some of the UI components crash, the rest can still interact.)

How to handle Uncaught Errors?

Since React 16, any error not caught by the error boundary will cause the entire React component tree to be uninstalled.

In some cases, leaving a bad UI there is worse than removing it completely. For example, in a product like Messenger, presenting a user with an abnormal UI might cause the user to send an error message to others. Similarly, for payment scenarios, instead of displaying the wrong payment amount, it’s better to just leave the page blank.

(So, the difficulty of using error boundaries is finding the right time to use them, and knowing when to use them and when not to use them, which is a philosophical problem.)

Note: Rename changes since React 15

React 15 has a method called unstable_handleError that supports limited error bounds. This method no longer works, since React 16 you need to change it to componentDidCatch in your code.