molefrog / wouter

🥢 A minimalist-friendly ~2.1KB routing for React and Preact
https://npm.im/wouter
The Unlicense
6.65k stars 152 forks source link

Accessing nested params with `useParams` #409

Closed pReya closed 3 months ago

pReya commented 8 months ago

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>
          </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 commented 8 months ago

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 commented 8 months ago

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 commented 8 months ago

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 commented 8 months ago

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

vicodinvic1 commented 6 months ago

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 commented 6 months ago

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

larpo commented 6 months ago

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>
    );
};
mattkrins commented 3 months ago

Kinda bizarre this is not already possible... This library is pretty much perfect in every way, except for this.

molefrog commented 3 months ago

@mattkrins I know... This feature isn't hard to implement, however this will require every Route in the app to subscribe to the context to get the outer parameters. It will mean that every route down the tree will re-render even if it was wrapped in memo. It's a performance trade-off that we will have to consider.

molefrog commented 3 months ago

Ok, actually I am wrong. Thoughts:

  1. Parameters only change when the current location changes (probably, I can't think of an edge-case)
  2. Every route is also subscribed to current location, so it will re-render anyway
molefrog commented 3 months ago

Released in https://github.com/molefrog/wouter/releases/tag/v3.3.0