Browser navigation broken by use of React Error Boundaries

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();
  }