This is a feature request to handle _NoMatch_ route in a more global generic way.
I have a codepen that is based on React Router Template that shows how a _NoMatch_ component is not rendered even if you go to a URL that has no match in the router configuration.
When I move the _NoMatch_ Route into AboutTopicsRoute Switch it all works fine. However, I don't want it there since my application might grow and there are different routes in different files and it gets confusing to move the _NoMatch_ around just so it renders correctly.
There are some solutions, like mentioned in a comment on issue#4685, out there but most of them rely on some sort of state being introduced and that seems to make things overly complicated.
I also find it strange that we all have to add some boilerplate code just to handle _NoMatch_ since it is a fairly basic case all applications using routing need to handle.
It would be simple to add a prop like <Route nomatch component={NoMatch} /> and if you need more control depending on the route that wasn't found you could simply use the render variation of Route like so
<Route
nomatch
render={
({ location }) => {
if(location.pathname.startsWith('/user'))
return <div>missing user nested</div>
return <div>something missing</div>
}
}
/>
This gives you complete freedom to handle no match in any way you see fit without any specific state being introduced.
Just use Route with a path of * at the end of your Switch. It will always match, but occurs last and will only be used if nothing else matches.
Thank you, @timdorr, for taking the time to look at the issue. However, your solution doesn't work.
Using the following route configuration will never render the _NoMatch_ component as you can see in my codepen
<Switch>
<Route exact path="/" component={Home}/>
<Switch>
<Route path="/about" component={About}/>
<Route path="/topics" component={Topics}/>
</Switch>
<Route path="*" component={()=>(<div>NoMatch</div>)} />
</Switch>
If I move <Route path="*" component={()=>(<div>NoMatch</div>)} /> into my nested Switch it will render and work as expected.
<Switch>
<Route exact path="/" component={Home}/>
<Switch>
<Route path="/about" component={About}/>
<Route path="/topics" component={Topics}/>
<Route path="*" component={()=>(<div>NoMatch</div>)} />
</Switch>
</Switch>
But if I add a route below the nested _Switch_ it will never be hit since * will catch all that is not above.
<Switch>
<Route exact path="/" component={Home}/>
<Switch>
<Route path="/about" component={About}/>
<Route path="/topics" component={Topics}/>
<Route path="*" component={()=>(<div>NoMatch</div>)} />
</Switch>
<Route path="/never" component={()=>(<div>Never hit :(</div>)} />
</Switch>
How do you suggest I move on? Simply move the * to the bottom of it all? Well, I tried that and ended up with this.
<Switch>
<Route exact path="/" component={Home}/>
<Switch>
<Route path="/about" component={About}/>
<Route path="/topics" component={Topics}/>
</Switch>
<Route path="/never" component={()=>(<div>Never hit :(</div>)} />
<Route path="*" component={()=>(<div>NoMatch</div>)} />
</Switch>
And now _Never hit :(_ and _NoMatch_ never renders...
Am I simply not getting it?
Nested <Switch>es are not supported. See docs:
All children of a
<Switch>should be<Route>or<Redirect>elements.
Try replacing the <Switch> in your AboutTopicsRoute with an array.
Thank you so much @taurose that was exactly what I needed. Very helpful comment.
I guess I simply didn't get it ;)
Isn't it the case that
I think something like this could be used instead of
import React from 'react';
import { Switch, Route } from 'react-router-dom';
import _404Component from '../Page/_404Component';
const SwitchWith404 = props => (
<Switch>
{props.children}
<Route component={_404Component} />
</Switch>
);
export default SwitchWith404;
And then instead of using
Most helpful comment
Just use Route with a path of * at the end of your Switch. It will always match, but occurs last and will only be used if nothing else matches.