Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

useRoute in nested Router with basepath cannot match absolute path #244

Open
waful opened this issue Apr 28, 2022 · 3 comments
Open

useRoute in nested Router with basepath cannot match absolute path #244

waful opened this issue Apr 28, 2022 · 3 comments

Comments

@waful
Copy link

waful commented Apr 28, 2022

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 Links 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?

@cbbfcd
Copy link
Contributor

cbbfcd commented May 9, 2022

@waful i have a doubt, why not distinguish by path, you need to use base? Maybe I didn't understand

@waful
Copy link
Author

waful commented Oct 2, 2022

@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.

@molefrog molefrog added the V3 Features that won't be released in v2, but planned for the next major release label Nov 22, 2023
@Bonfims
Copy link

Bonfims commented Nov 24, 2023

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.

@molefrog molefrog removed the V3 Features that won't be released in v2, but planned for the next major release label Mar 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants