Navigating to a 404 Route with Reach Router
Asked Answered
B

3

5

I have the following routing config:

<Router>
 <NotFound default />
 <ResourcesContainer path="/resources" />
 <ResourceContainer path="/resources/:id" />
 ...
</Router>

This catches any routes that are not handled, and renders the <NotFound /> component at the URL that wasn't found, so if I type example.com/blah, I see the <NotFound /> component rendered, and in the address bar I see example.com/blah. I also use this URL on the page to display a message:

The page 'example/blah' was not found.

So far so good. However I also need to handle 404s from within in /resources/* routes. My <ResourcesContainer/> component uses the last part of the path to hit a GraphQL API for a resource with that id. If the API returns to tell the client that resource doesn't exist, I would like to mimic the behaviour outlined above. However, I don't have a page to navigate to. I could duplicate the <NotFound /> route and give it an explicit path of /404, then navigate to that. However the URL would then be /404 and not the original resources/* path that wasn't found.

The following solves part of the problem, giving me a page to redirect ot, but means the URL is rewritten to /404 in all cases:

<Router>
 <ResourcesContainer path="/resources" />
 <ResourceContainer path="/resources/:id" />
 <NotFound path="/404" />
 <Redirect noThrow from="*" to="/404" />
 ...
</Router>

How can I set this up so that I can navigate to the <NotFound /> route without losing the original URL?

Bleary answered 9/12, 2020 at 13:36 Comment(0)
M
3

Your best option is to change render method of ResourceContainer to render NotFound if resource is not found.

However, if you don't want to make changes to ResourceContainer, you can wrap it with an error boundary like this:

class NotFoundErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { notFound: false };
  }

  static getDerivedStateFromError(error) {
    // Filter which kind of errors you want to show the error boundary for
    return { notFound: true };
  }

  render() {
    if (this.state.notFound) {
      // You can render any custom fallback UI
      return <NotFound />;
    }

    return this.props.children; 
  }
}

And use it like:

<NotFoundErrorBoundary>
 <ResourceContainer path="/resources/:id" />
</NotFoundErrorBoundary>

Your ResourceContainer can throw an error NotFoundErrorBoundary can identify and that can signal that resource is not found and it should render NotFound page instead of the children.

To be clear, I am not encouraging you to use ErrorBoundary. In my opinion, it will overcomplicate things. I just present you the information, how you use it is up to you. Also it may be useful to you in another context depending on the use case.

Manaker answered 15/12, 2020 at 10:36 Comment(2)
or if status=4** (404 --not sure if this framework allows wildcards in the ant matchers.to handle all related issues.)Quarterly
I have given the bounty to this answer, as I like the ErrorBoundary solution because it offers a central location to handle any 404s, with the option to handle more locally in cases where something more specific needs to be rendered. However, note that you will run into issues testing this with Cypress which doesn't play well with Error Boundaries due to both it and React using a global error handler.Bleary
D
3

Error boundary isn't a good option to handle it. The official website encourages us use nested route philosophy e.g. The best practice is use a <Router> inside of each child: Root:

<Router>
 <NotFound default />
 <ResourcesContainer path="/resources/*" />
 ...
</Router>

ResourcesContainer.jsx:

<Router>
 <NotFound default />
 <ResourcesList path="/" />
 <ResourceDetail path="/:id" />
 ...
</Router>

How to render <NotFound /> if we get 404 in API call? It's easy:

if (API.error) return <NotFound />;
if (API.data) return ...;

How to simplify it? We can have a wraper for with like this: RouterWIthNotFound

<Router>
 <NotFound default />
 {children}
</Router>
Dormer answered 19/12, 2020 at 17:45 Comment(4)
You haven't solved the problem the question sets out. The <NotFound/> component will never be rendered as all paths will be caught by /:id.Bleary
If he is only bounding a single error, I would say it is the best option. Every use case deserves its own solution. Otherwise we would all be using wix right now.. XDQuarterly
@NathanToulbert I agree that every use-case deserves its own solution, but in this case this is not a solution to the use-case I outline in the question.Bleary
@NathanToulbert I added How to render <NotFound /> if we get 404 in API call?.Dormer
O
2

This is not a route problem in my opinion. The component that renders /resources/:id basically has three states: loading, foundData and 404. If you just render different components for each state then you can just render the same 404 component. just simple if you don't want to change the url, don't navigate.

Outstand answered 14/12, 2020 at 14:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.