Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Browser navigation broken by use of React Error Boundaries

Tags:

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?

like image 877
Underwater_developer Avatar asked Jan 05 '18 21:01

Underwater_developer


People also ask

How do you reset error boundaries in React?

A vaccine with react-error-boundary: With this function, we can explicitly reset the state of ErrorBoundary by clicking on the Try again button.

How are error boundaries handled in React?

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.

How many error boundaries We can create and use in React?

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.

How do you handle error boundaries in functional components?

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.


2 Answers

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,     });   } }); 
like image 63
jdavies Avatar answered Sep 21 '22 09:09

jdavies


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();   } 
like image 26
pfarnach Avatar answered Sep 21 '22 09:09

pfarnach