When an error is thrown in our React 16 codebase, it is caught by our top-level error boundary. The ErrorBoundary
component happily renders an error page when this happens.
Where the ErrorBoundary sits
return ( <Provider store={configureStore()}> <ErrorBoundary> <Router history={browserHistory}>{routes}</Router> </ErrorBoundary> </Provider> )
However, when navigating back using the browser back button (one click), the URL changes in the address but the page does not update.
I have tried shifting the error boundary down the component tree but this issue persists.
Any clues on where this issue lies?
A vaccine with react-error-boundary: With this function, we can explicitly reset the state of ErrorBoundary by clicking on the Try again button.
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.
Now that Error Boundaries are available since React version 16, it's generally advisable to use at least one Error Boundary at the root of your app (e.g., the App. js file). This will prevent users from seeing a blank HTML page and perhaps see a nice fallback UI instead.
In order to use Error Boundary in Functional Component, I use react-error-boundary. When we run this application, we will get a nice error display form the content of ErrorHandler component. React error boundary catches any error from the components below them in the tree.
The op has probably found a resolution by now, but for the benefit of anyone else having this issue I'll explain why I think its happening and what can be done to resolve it.
This is probably occurring due to the conditional rendering in the ErrorBoundary rendering the error message even though the history has changed.
Although not shown above, the render method in the ErrorBoundary is probably similar to this:
render() { if (this.state.hasError) { return <h1>An error has occurred.</h1> } return this.props.children; }
Where hasError is being set in the componentDidCatch
lifecycle method.
Once the state in the ErrorBoundary has been set it will always render the error message until the state changes (hasError to false in the example above). The child components (the Router component in this case) will not be rendered, even when the history changes.
To resolve this, make use of the react-router withRouter higher order component, by wrapping the export of the ErrorBoundary to give it access to the history via the props:
export default withRouter(ErrorBoundary);
In the ErrorBoundary constructor retrieve the history from the props and setup a handler to listen for changes to the current location using history.listen. When the location changes (back button clicked etc.) if the component is in an error state, it is cleared enabling the children to be rendered again.
const { history } = this.props; history.listen((location, action) => { if (this.state.hasError) { this.setState({ hasError: false, }); } });
To add to jdavies' answer above, make sure you register the history listener in a componentDidMount
or useEffect
(using []
to denote it has no dependencies), and unregister it in a componentWillUnmount
or useEffect
return statement, otherwise you may run into issues with setState
getting called in an unmounted component.
Example:
componentDidMount() { this.unlisten = this.props.history.listen((location, action) => { if (this.state.hasError) { this.setState({ hasError: false }); } }); } componentWillUnmount() { this.unlisten(); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With