I want to create an error boundary that would prevent my entire app from crashing but rather inform the user that something went wrong and give him an option to reload the page. My error boundary component:
import React, { Fragment, PureComponent } from "react";
import { withRouter, RouteComponentProps } from "react-router";
type Props = RouteComponentProps;
type State = { hasError: boolean; };
class RootErrorBoundary extends PureComponent<Props, State> {
state: State = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
reloadPage = () => {
this.setState({ hasError: false });
const { history } = this.props;
history.go(0);
};
renderOnError = () => {
return (
<Fragment>
Somthing went wrong!
<button onClick={this.reloadPage}>Click here to reload</button>
</Fragment>
);
};
render() {
if (this.state.hasError) {
return this.renderOnError();
}
return this.props.children;
}
}
export default withRouter(RootErrorBoundary);
I implement it like this:
// index.jsx
// ...
ReactDOM.render(
<Provider store={store}>
<Router history={history}>
<StylesProvider injectFirst>
<ConnectedThemeProvider>
<RootErrorBoundary>
<App />
</RootErrorBoundary>
</ConnectedThemeProvider>
</StylesProvider>
</Router>
</Provider>,
rootElement
);
And then, somewhere deep inside my App component tree, I define a function to manually trigger the error (for testing/prototyping purposes):
const [hasError, setHasError] = useState(false);
(window as any).kaboom = () => setHasError(true);
if (hasError) {
setHasError(false);
throw new Error("CUSTOM ERROR");
}
When I call window.kaboom()
from console, my error boundary triggers properly. But once I click the reload button, instead of the page refreshing, the same error then gets caught by cra's error overlay and prints the stack trace:
Error: CUSTOM ERROR
TemplateTable
FILE_WITH_ERROR.tsx:96
93 | (window as any).kaboom = () => setHasError(true);
94 | if (hasError) {
95 | setHasError(false);
> 96 | throw new Error("CUSTOM ERROR");
| ^
97 | }
98 |
99 | return (
View compiled
▶ 17 stack frames were collapsed.
TemplateTable.window.kaboom
FILE_WITH_ERROR.tsx:93
90 | );
91 |
92 | const [hasError, setHasError] = useState(false);
> 93 | (window as any).kaboom = () => setHasError(true);
| ^
94 | if (hasError) {
95 | setHasError(false);
96 | throw new Error("CUSTOM ERROR");
View compiled
(anonymous function)
<anonymous>:1:1
Why is that? I was expecting my custom error boundary to catch the error and not let it propagate any further. And how can I achieve what I want and properly test my solution?
I was wrong in my expectations. Error overlay cannot be disabled in development mode and catching an error does not prevent it. It also was not triggered by a button click as I thought - it appearead after a few seconds regardless (I just associated it with clicking the button in my head).
All i have to do to test my boundary is press escape
once it pops out to close it again.
Credit goest to this answer: https://stackoverflow.com/a/47400249/10467064