wouter
wouter copied to clipboard
useRoute in nested Router with basepath cannot match absolute path
When you use useRoute
in a nested Router
with basepath
specified, the path matching only matches the path portion after the base path. For example, in the following configuration,
<TopLevelRouter>
<ChildRouter base='/test'>
...
<Component />
...
</ChildRouter>
<ChildRouter base='/othertest'>
...
<Component />
...
</ChildRouter>
</TopLevelRouter>
The useRoute
inside Component
cannot match absolute paths like ~/test/:rest*
(the way Link
s can), meaning there is no way to figure out if the current path is under /test
or /othertest
. Looking at the matcher source code, this is just a functionality that has not been implemented. But from a conceptual, functional point of view, is this considered a bug? I would think that there should be a way to match full path without resorting to window.location.href
?
@waful i have a doubt, why not distinguish by path, you need to use base? Maybe I didn't understand
@cbbfcd Elaborated contrived example:
<TopLevelRouter>
<ChildRouter base='/userTypeA'>
...
<Route path='/settings' component={SharedSettingsComponent} />
<Route path='/otherSharedPage' component={SharedPageComponent} />
<Route path='/featureA' component={FeatureAComponent} />
<Route path='/' component={TypeAComponent} />
...
</ChildRouter>
<ChildRouter base='/userTypeB'>
...
<Route path='/settings' component={SharedSettingsComponent} />
<Route path='/otherSharedPage' component={SharedPageComponent} />
<Route path='/' component={TypeBComponent} />
...
</ChildRouter>
</TopLevelRouter>
I have 2 user types with some shared pages and some type specific pages nested in their respective routers. The settings page for both users are for the most part identical with minor styling/wording changes, so I want to use the same component with a switch flag instead of creating two completely distinct components. I want to be able to identify the nested base route I'm currently in from within SharedSettingsComponent
, but the useRoute
inside it can only match /settings
and not /userType(A|B)/settings
. No matter if you're in /userTypeA/settings
or /userTypeB/settings
you can only match the same path /settings
. <Link>
handles this today already, you can <Link href='/otherSharedPage'>
from inside SharedSettingsComponent
and it will go to /userType(A|B)/otherSharedPage
based on which ChildRouter
you're in, and you can link outside of the current nested base path for example with <Link href='~/userTypeA/otherSharedPage'>
even if you're in /userTypeB
. But useRoute
does not have a similar capability to interact outside of its current nested router context via the ~
prefix.
EDIT: upon revisiting this problem I wonder if I could just use useRouter
and grab router.base
for this specific scenario. However this is just another workaround like using window.location.href
and the issue remains that useRoute
has no way to match absolute route. The useRouter
workaround would not work with multiple nested routers because it would only grab the immediate base path and not the full base bath.
i want made something similar, look exemple:
function Item({ label, href }) {
const [match] = useRoute(href)
return (
match ?
<li className='selected'><Link href={href}>{label}</Link></li>
:
<li><Link href={href}>{label}</Link></li>
);
};
function MainMenu() {
return (
<ul>
<Item label="A" href="section-A" />
<Item label="B" href="section-B" />
</ul>
)
};
function SubMenu() {
return (
<>
<Item label="1" href="/option-1" />
<Item label="2" href="/option-2" />
</>
)
};
function App() {
return (
<Router>
<MainMenu />
<Switch>
<Route path="/section-A">
<NestedRouter base="/section-A">
<SubMenu />
<Switch>
<Route path="/option-1">
<h1>Option 1 from Section A</h1>
</Route>
<Route path="/option-2">
<h1>Option 2 from Section A</h1>
</Route>
</Switch>
</NestedRouter>
</Route>
<Route path="/section-B">
<NestedRouter base="/section-B">
<SubMenu />
<Switch>
<Route path="/option-1">
<h1>Option 1 from Section B</h1>
</Route>
<Route path="/option-2">
<h1>Option 2 from Section B</h1>
</Route>
</Switch>
</NestedRouter>
</Route>
</Switch>
</Router>
)
};
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>
)
The
NestedRouter
is from example on docs.
So what i want is that my Item
component can figure out if he is a selected item by useRoute
, on first level it works, but on the others doest.
If i have selected a item on MainMenu
, he become selected
, but if i select another item on SubMenu
, the previous selected item become unselected. Thinking that current selected item from SubMenu
is inside a nested router, the item on MainMenu
need be selected too, but useRoute
doesnt match.
/section-A
-> only MainMenu
item is selected and its OK.
/section-A/option1
-> item from MainMenu
is unselected and only SubMenu
item is selected, but i think that is a wrong.
I think that useRoute
called inside Item
from MainMenu
should match with href /section-A/option-1
, by the routing structure.
I figure out how to work around, with:
const [match] = useRoute(href + "/:any*");
So the Item
component became:
function Item({ label, href }) {
const [match] = useRoute(href + "/:any*");
return (
match ?
<li className='selected'><Link href={href}>{label}</Link></li>
:
<li><Link href={href}>{label}</Link></li>
);
};
Now he matches with base path froim nested router and routes on sub menu.