Reputation: 31565
While trying to make nested routes in react router dom v5 I found this answer that explains how to do it very well
(please take a look at the code here because it's a little bit difference from the answer mentioned above)
const NotFound = () => <h1>Not Found</h1>;
function Layouts() {
return (
<Switch>
<Route path="/auth" component={AuthLayout} />
<Route path="/app" component={AppLayout} />
<Route path="/" component={NotFound} />
</Switch>
);
}
const Signup = () => <p>Login</p>;
const Login = () => <p>Sign up</p>;
function AuthLayout() {
return (
<div>
<h1>Auth Layout</h1>
<Route path="/auth/signup" exact component={Signup} />
<Route path="/auth/login" exact component={Login} />
{/* Commenting this because I want to go to NotFound component */}
{/* <Redirect from="/auth" to="/auth/login" exact /> */}
</div>
);
}
const Home = () => <p>Home</p>;
const Dashboard = () => <p>Dashboard</p>;
function AppLayout() {
return (
<div>
<h1>App Layout</h1>
<Route path="/app/home" exact component={Home} />
<Route path="/app/dashboard" exact component={Dashboard} />
{/* Commenting this because I want to go to NotFound component */}
{/* Redirect from="/app" to="/app/home" exact /> */}
</div>
);
}
But this have one problem, that if you go to a route with /app/somethingnotfound
it won't go to <Route path="/" component={NotFound} />
, it will "stay inside" AppLayout
and render no route.
How can I make /app/somethingnotfound
go to <Route path="/" component={NotFound} />
in this case?
Just to be more clear: I don't want to just add <Route component={NotFound} />
inside AuthLayout
and AppLayout
because it will render other things. What I really need is to show the top level NotFound
.
Upvotes: 2
Views: 1877
Reputation: 19813
Not found component usually works like this:
<Router>
<Switch>
<Route exact path="/auth" component={AuthLayout} />
<Route exact path="/app" component={AppLayout} />
<Route component={NotFound} />
</Switch>
</Router>
But you cannot mark /auth
and /app
as exact
as they contain nested routes. So, you should do:
<Router>
<Switch>
<Route path="/auth" component={AuthLayout} />
<Route path="/app" component={AppLayout} />
<Route exact path="/404" component={NotFound} />
<Redirect to='/404' />
</Switch>
</Router>
and your component (e.g. AppLayout
) with nested routes:
<>
<h1>App Layout</h1>
<Switch>
<Route path="/app/home" exact component={Home} />
<Route path="/app/dashboard" exact component={Dashboard} />
<Redirect to="/404" />
</Switch>
</>
Upvotes: 3
Reputation: 6837
<Route path="/" component={NotFound} />
doesn't match for all the nested that are not implemented, because the Switch
in the Layouts
component will only render the first Route
child that matches the pathname
. In your case, it matches the route with AppLayout
component when you go the path /app/somethingdoesntexist
that is why it only renders the AppLayout
component.
Solution
It is better to implement a NotFound
route for each layout using nested Switch
components, For example
function AppLayout() {
const { path, url } = useRouteMatch();
return (
<Switch>
<Route path={`${path}/home`} exact component={Home} />
<Route path={`${path}/dashboard`} exact component={Dashboard} />
<Route path="*" component={NotFound} />
</Switch>
);
}
In the case above we got the matched path by using useRouteMatch
hook and nest another Switch
component that will render any nested route that matches from the path
and we provide a fallback Route
specifying the path as "*"
which will render the NotFound
component when there is no match found.
Here is a complete example of the solution codesandbox.
You can also see the nesting example provided in the docs.
Upvotes: 0
Reputation: 31565
While looking in github issues I found out this solution, you create a "global not found page" where you pass through the state if the route isn't found, and just render that instead of the other routes.
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>
);
};
Upvotes: 0