The intuition about "not found" for me is changing with client side routing, but we still need to show people how to do the "old way". I'm going to write a guide for this, just making some notes here:
Hey Ryan.
I took your advice and tried to recreate it with the modal example.
Maybe this is a good starter for the doc :).
Thank you ! 馃挴
import React, { Component } from 'react';
import { Switch, Route, Redirect, Link } from 'react-router-dom';
const Frontend = props =>
<div>
<h2>Frontend</h2>
{props.children}
<footer>Bottom</footer>
</div>
const Home = () => <div><h1>Home</h1></div>;
const User = () => <div><h1>User</h1></div>;
const Error = () => <div><h1>Error</h1></div>
class ErrorSwitch extends Component {
previousLocation = this.props.location
componentWillUpdate(nextProps) {
const { location } = this.props;
if (nextProps.history.action !== 'POP'
&& (!location.state || !location.state.error)) {
this.previousLocation = this.props.location
};
}
render() {
const { location } = this.props;
const isError = !!(
location.state &&
location.state.error &&
this.previousLocation !== location // not initial render
)
return (
<div>
{
isError
? <Route component={Error} />
: <Frontend>
<p><Link to="/">Root</Link></p>
<p><Link to="/user">User</Link></p>
<p><Link to="/the-route-is-swiggity-swoute">Swiggity swooty</Link></p>
<hr />
<Switch location={isError ? this.previousLocation : location}>
<Route exact path='/' component={Home}/>
<Route exact path='/user' component={User}/>
<Redirect to={{
state: { error: true }
}} />
</Switch>
</Frontend>}
</div>
)
}
}
class App extends Component {
render() {
return <Route component={ErrorSwitch} />
}
}
export default App;
It would also be helpful to see some example of handling non-matches in the route-config sample. I got part of the way there by wrapping the map()
call in a <Switch>
, but turns out the catch-all <Route component={Some404Page} />
doesn't work in that scenario.
I have a simple video demonstrating the "Not Found" page. Hopefully this is useful.
https://www.youtube.com/watch?v=sUauVes2aC4&list=PL8vZpHuqa_hPpKUHFlyPkiI4MPexAMhvc&index=3
Would love to see this guide, especially solution for the first case in the list.
I've extracted a small sample from my app that demonstrates the first case. A nested "not-found" page.
@ryanflorence suggested this solution in https://github.com/ReactTraining/react-router/issues/4685#issuecomment-285877182
The trick is to redirect to the same page including state (RouteNotFound
) and use this state to determine what to render (CaptureRouteNotFound
), the application or the "not found" page. The example also shows how to use an AppShell
around the application but not around the "not found" page. This AppShell
is only mounted once.
I've typed this up on GitHub, so may contain typos.
const NotFound = () => <div className="not_found"><h1>Not Found</h1></div>;
const RouteNotFound = () => <Redirect to={{ state: { notFoundError: true } }} />;
const CaptureRouteNotFound = withRouter(({children, location}) => {
return location && location.state && location.state.notFoundError ? <NotFound /> : children;
});
const Settings = () => {
return (
<Switch>
<Route path="/settings/account" render={() => <h1>Account Settings</h1>} />
<Route path="/settings/profile" render={() => <h1>Profile Settings</h1>} />
<RouteNotFound />
</Switch>
);
};
const AppShell = ({children}) => {
return (
<div className="application">
<header>Application</header>
{children}
</div>
);
};
const Application = () => {
return (
<Router>
<CaptureRouteNotFound>
<AppShell>
<Switch>
<Route path="/settings" render={() => <Settings />} />
<Route path="/profile" render={() => <h1>User Profile</h1>} />
<RouteNotFound />
</Switch>
</AppShell>
</CaptureRouteNotFound>
</Router>
);
};
@s0meone that's a great example, thank you!
@s0meone its best solution for me! big thx
I ended up combining a couple of the solutions here to build a "withStatus" higher-order component.
@s0meone I am struggling to understand the benefit of your approach. Since RouteNotFound
needs to be used in every sub route, why not use <Route component={NotFound} />
directly and avoid state etc?
@iamvanja I'm using the state to determine if we need to render the AppShell
higher up in the tree.
Because the example I'm giving shows a way to show full screen not found messages. So we either render the app within AppShell
or a full screen not found page without the AppShell
.
@s0meone That was the same idea I had with the example on top :).
Edit: Sometimes full screen error messages are cooler :D.
@s0meone I see now how this pattern is useful and powerful. Thanks!
I tried @s0meone 's approach, but location.state is always undefined, whether I'm hitting a defined route or not.
I'm a little confused - how do I create a 404 page without a <Switch>
? I match and render multiple components based on the path, so I can't just put all of my app's routes in a Switch. Am I misunderstanding? Can I maybe just Switch my app routes as a block (with multiple matches permitted inside), and if none match then 404? Or what?
I am not quite sure what @s0meone 's HOC state component will give me over just rendering the <NotFound />
component? I'll still have to put it in each component that has routing in it so it's hardly a global catch-all.. Is it really impossible to achieve this with React Router v4?
@s0meone i have created a snippet https://codesandbox.io/s/j4y482yl43 for some reason i can't use your technique can you please tell me is it good approach
@datoml i would like to have your suggestion also
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
Most helpful comment
I've extracted a small sample from my app that demonstrates the first case. A nested "not-found" page.
@ryanflorence suggested this solution in https://github.com/ReactTraining/react-router/issues/4685#issuecomment-285877182
The trick is to redirect to the same page including state (
RouteNotFound
) and use this state to determine what to render (CaptureRouteNotFound
), the application or the "not found" page. The example also shows how to use anAppShell
around the application but not around the "not found" page. ThisAppShell
is only mounted once.I've typed this up on GitHub, so may contain typos.