fenok / react-router-typesafe-routes

Comprehensive and extensible type-safe routes for React Router v6 with first-class support for nested routes and param validation.
MIT License
145 stars 3 forks source link

Unexpectedly seeing searchParameters pushed through to child routes #51

Closed sloanesturz closed 4 months ago

sloanesturz commented 4 months ago

If I have a ROUTES like this:

const TEST_ROUTES = {
  EXAMPLE: route(
    'example',
    { searchParams: { q: string() } },
    { DETAILS: route(':id') }
  ),
};

I would expect to want to navigate to example?q=My+query but not example/1?q=My+query. The typescript is letting this go through. If your application is built correctly, this is going to end up rendering fine but will have an awkward URL (and is a sign that your link is being constructed wrong).

Eg: this does not match the @ts-expect-error I want --

// @ts-expect-error - extra search parameters here.
TEST_ROUTES.EXAMPLE.DETAILS.buildPath({ id: '1' }, { q: 'my query' })

Is this a bug or expected behavior? If it's expected, can you help me understand the scenario it's supporting?

fenok commented 4 months ago

This is an intended feature that matches how nested routes work in React Router.

Each route part usually corresponds to some UI on the screen (check their visualization), which may use some search (and other) params. So the full route has to include params from all its parts.

I see two ways to achieve what you want.

  1. Move search (and other) params to a nested route:
const TEST_ROUTES = {
  EXAMPLE: route(
    'example',
    {},
    {
      INDEX: route('', { searchParams: { q: string() } }),
      DETAILS: route(':id'),
    }
  ),
};
<Routes>
  <Route path={TEST_ROUTES.EXAMPLE.path}>
    <Route index element={<Example />} />
    <Route path={TEST_ROUTES.EXAMPLE.DETAILS.path} element={<ExampleDetails />} />
  </Route>
</Routes>
  1. Unnest the routes entirely:
const TEST_ROUTES = {
  EXAMPLE: route('example', { searchParams: { q: string() } }),
  EXAMPLE_DETAILS: route('example/:id'),
};
<Routes>
  <Route path={TEST_ROUTES.EXAMPLE.path} element={<Example />} />
  <Route path={TEST_ROUTES.EXAMPLE_DETAILS.path} element={<ExampleDetails />} />
</Routes>
sloanesturz commented 4 months ago

I see! I have my <Routes /> unnested like option 2 already, so I think that matching that pattern in ROUTES is what I want.

Thank you for pointing me in the right direction!