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

Accessing nested params with useParams #409

Open
pReya opened this issue Jan 23, 2024 · 8 comments
Open

Accessing nested params with useParams #409

pReya opened this issue Jan 23, 2024 · 8 comments
Labels

Comments

@pReya
Copy link

pReya commented Jan 23, 2024

Hello, I have a component setup that looks something like this:

<Provider value={client}>
  <!-- some more stuff here -->
  <Route path="/admin" nest>
    <Switch>
      <Route path="/">
        <RedirectToCompany />
      </Route>
      <Route path="/:companyId" nest>
        <AdminLayout menu={menu}>
          <Switch>
            <Route path="/seminare">
              <SeminarPage />
            </Route>
            <Route path="/">
              <Redirect to="/seminare" />
            </Route>
          </Switch>
        </AdminLayout>
      </Route>
    </Switch>
  </Route>
</Provider>

I'd like to access my companyId param within my <SeminarPage> component, which is nested inside another route, via useParams(). However I always get an empty object. I assume because useParams() will always use the params from the closest ancestor <Route> component. With nested routes, is there any way to access "higher up" params?

@molefrog
Copy link
Owner

Hi @pReya, I don't think it's possible at the moment. In the current design, the context where parameters are stored gets overwritten in the Route, so parameters don't inherit.

@larpo
Copy link

larpo commented Jan 30, 2024

The way it currently works is pretty counterintuitive and makes using nested routes not an option in many cases.

Intuitively something like this should work, and does work at least in react-router.

<Route path="/:dataset" nest>
    <Route path="/">
        <ChartPicker />
    </Route>
    <Route path="/:type">
        <Chart />
    </Route>
</Route>;

Merging existing params to the params context in Route seems pretty small change, is that something that could be added at this point?

@molefrog
Copy link
Owner

molefrog commented Jan 30, 2024

I see. Your point is fair enough. I'm finalising everything for the v3 release (it's been in the rc for over 2 m already, so really want to publish it soon). Since it isn't a breaking change, perhaps we can get back to it in the next minor release?

@larpo
Copy link

larpo commented Jan 30, 2024

Minor release sounds reasonable, bit too late at this point with rc3 👍

@vicodinvic1
Copy link

The way it currently works is pretty counterintuitive and makes using nested routes not an option in many cases.

Intuitively something like this should work, and does work at least in react-router.

<Route path="/:dataset" nest>
    <Route path="/">
        <ChartPicker />
    </Route>
    <Route path="/:type">
        <Chart />
    </Route>
</Route>;

Merging existing params to the params context in Route seems pretty small change, is that something that could be added at this point?

it doesn't work like that, useParams returns just an empty object if you try to use it inside of a nested nest

@vicodinvic1
Copy link

@molefrog please add this feature, otherwise I will have to go back to react-router-dom v5 :(

@vicodinvic1
Copy link

I wrote a custom useParams hook you can use, until wouter's one doesn't work properly.
It requires to have an additional Context.Provider though, that should wraps the whole page to be able to have access to route path you defined here <Route path='/some-path' /> to be able to use useParams hook anywhere inside of a page

Wrapper:

import React from 'react'

export const PageContext = React.createContext()

function YourPageWrapperComponent (props) {
    const { Page, path = 'books/:id/pages/:pageId/lines/:lineId' , ...restProps } = props

   ...probably here is some wrapper logic you have placed...

   return (
      <Route path={path}> // route path
        <PageContext.Provider value={path}> // same route path
           <Page /> // your page where you can use custom useParams hook now
        </PageContext.Provider>
     </Route>
    )
}

the hook itself:

import React from 'react'

import { PageContext } from 'path/to/context'

export default function useParams () {
  const routePath = React.useContext(PageContext)
  const slicedPathname = window.location.pathname.split('/').slice(1)
  const routeParams = routePath.split('/').slice(1)
  const params = {}

  for (const param of routeParams) {
    if (param.startsWith(':')) {
      const index = routeParams.indexOf(param)

      if (~index) {
        const key = param.slice(1)

        params[key] = slicedPathname[index]
      }
    }
  }

  return params
}

@larpo
Copy link

larpo commented Mar 14, 2024

We are using these light wrappers, seem to work fine (with the downside of having to import the custom implementations instead of wouter):

import {useParams, Route} from "wouter";

type ParentParamsContextType = Record<string, string>;
const ParentParamsContext = createContext<ParentParamsContextType>({});

export function useNestedParams<T extends ParentParamsContextType>(): T {
    const parentParams = useContext(ParentParamsContext);
    const params = useParams<T>();
    return {...parentParams, ...params} as T;
}

export const CustomRoute: typeof Route = props => {
    const params = useNestedParams();
    return (
        <ParentParamsContext.Provider value={params}>
            <Route {...props} />
        </ParentParamsContext.Provider>
    );
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants