2

I am learning React and am unable to get an Error Boundary catching a network connection error thrown by an axios http request in a child component when the API server is unavailable.

I can see that the error is logged in the catch handler of the child component, however the throw statement is not caught by the ErrorBoundary component.

Any ideas how to get the Error Boundary catching an error thrown from componentDidMount lifecycle method?

Parent - App.tsx

export class App extends React.Component<AppProps, {}> {
  public render(): JSX.Element {
    return (
      <div>
        <ErrorBoundary>
          <AppBar color="primary" position="static">
            <Toolbar>
              <TypoGraphy variant="h6" color="inherit">
                Course App
              </TypoGraphy>
              <NavBar />
            </Toolbar>
          </AppBar>
          <CourseList />
        </ErrorBoundary>
      </div>
    );
  }
}

Child - CourseList.tsx

  componentDidMount(): void {
    console.log('CourseList::componentDidMount');

    const dataSource: RestDataCourse = new RestDataCourse();
    dataSource
      .getCourses()
      .then(courseList => {
        this.setState({ courses: courseList });
      })
      .catch(error => {
        console.error(`Error retrieving courses => ${JSON.stringify(error)}`);
        throw error;
      });
  }

ErrorBoundary

import * as React from 'react';

interface ErrorBoundaryProps {
  hasError: boolean;
  error: Error;
  info: React.ErrorInfo;
}
export default class ErrorBoundary extends React.Component<
  {},
  ErrorBoundaryProps
> {
  constructor(props: ErrorBoundaryProps) {
    super(props);
    this.state = { hasError: false, error: undefined, info: undefined };
  }

  componentDidCatch(error: any, info: any): void {
    console.log('ErrorBoundary has encountered an error');
    this.setState({ hasError: true, error: error, info: info });
  }

  render(): {} {
    if (this.state.hasError) {
      return (
        <div id="errorModal" className="modal">
          <div className="modal-content">
            <span className="close">&times;</span>
            <h2>Application Crash</h2>
            <p>Error encountered in application.</p>
            {this.state.error}
          </div>
        </div>
      );
    }

    // Render children if no error
    return this.props.children;
  }
}
anon_dcs3spp
  • 2,342
  • 2
  • 28
  • 62

1 Answers1

3

Error boundaries are designed to catch errors during rendering, in lifecycle methods, and in constructors of the whole tree below them.

If you want to catch unhandled promise errors globally (instead of handling it directly in component), you can use window.onunhandledrejection.

window.onunhandledrejection = function(e) {
  console.log(e.reason);
}

but it's not supported in all browsers by default.

Little hacky solution would be to call this.setState(() => { throw err; }); in catch of your promise.

zhuber
  • 5,364
  • 3
  • 30
  • 63
  • Thanks @zuber, I tried throwing an error in render method of child component and it does indeed get caught by the ErrorBoundary component :) On the React documentation it states that Error boundaries catch errors during rendering, in lifecycle methods, and in constructors of the whole tree below them. Shouldn't componentDidMount trigger an error to be caught by ErrorBoundary because it is a lifecycle method? – anon_dcs3spp Oct 07 '19 at 10:24
  • My bad, I'll correct the answer - but the main problem here is that error occurs in promise. – zhuber Oct 07 '19 at 10:30
  • Little hacky but you could call - this.setState(() => { throw err; }); Problem is that you'd have to do this in every catch. – zhuber Oct 07 '19 at 10:34