Nested routing broken by intermediate component
ukaaa opened this issue · comments
I am trying to achieve nested routing but with a custom component in between.
The snippet below does not give the expected result. When I click on the first link/a-base-page
it renders the expected <Route path="/">SubPageA</Route>
. But when I click on the second link /a-base-page/sub-page-1
it falls back to the <Route path="*">
. I was expecting it to render <Route path="/sub-page-1">SubPageA-1</Route>
.
It seems that the additional level introduced by ProtectedRoute
breaks the nested routing.
If I replace <ProtectedRoute path="/a-base-page" childRoutes={ChildRoutes} />
with <Route path="/a-base-page" component={ChildRoutes} nest />
it works again.
How can I make this work? I am the owner of ChildRoutes
. But ProtectedRoute
and App
are owned by another team. The goal is to decouple our routing.
Code sandbox: https://codesandbox.io/p/sandbox/wouter-3-0-0-issue-410-94fp9f
function ChildRoutes() {
return (
<Switch>
<Route path="/sub-page-1">SubPageA-1</Route>
<Route path="/sub-page-2">SubPageA-2</Route>
<Route path="/">SubPageA</Route>
<Route>Default route for switch nested in A</Route>
</Switch>
);
}
function ProtectedRoute({ path = "", childRoutes: ChildRoutes }) {
const canAccess = true; // some logic dependant on the signed-in user
return canAccess ? (
<Route path={path} nest>
<ChildRoutes />
</Route>
) : (
<Redirect to="/" />
);
}
function App() {
return (
<div className="App">
<nav>
<ActiveLink href="/a-base-page">A (/)</ActiveLink>
<ActiveLink href="/a-base-page/sub-page-1">A (1)</ActiveLink>
<ActiveLink href="/a-base-page/random-page">A (404)</ActiveLink>
</nav>
<main>
<Switch>
<Route path="/">HomePage</Route>
<ProtectedRoute path="/a-base-page" childRoutes={ChildRoutes} />
<Route path="*">
{(params) => <>404! Params is {JSON.stringify(params)}</>}
</Route>
</Switch>
</main>
</div>
);
}
Switch
can only work with direct children, because it iterates over JSX so matching is done before child components are even rendered. This is by design and the reason was to keep the size small, otherwise we had to wrap everything in extra context. I'm not sure how to explain this better, but maybe the source code can help.
How you can fix this: move canAccess
inside your App
component and rewrite the switch like so:
<Switch>
<Route path="/">HomePage</Route>
{canAccess ? <Route path="/a-base-page" nest><ChildRoutes /></Route> : <Redirect to="/" />}
<Route path="*">
{(params) => <>404! Params is {JSON.stringify(params)}</>}
</Route>
</Switch>
Thank you for getting back to me so quickly. Your reply inspired me to rewrite a piece of the code. It works now 👍