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

from does not allow route-less paths #3010

Open
nstepien opened this issue Dec 14, 2024 · 10 comments
Open

from does not allow route-less paths #3010

nstepien opened this issue Dec 14, 2024 · 10 comments
Labels
types Changes to the typescript types

Comments

@nstepien
Copy link
Contributor

Which project does this relate to?

Router

Describe the bug

Setting a from for a parent path that does not have a route results in a type and runtime error.

Your Example Website or App

https://stackblitz.com/edit/github-rkuq9rkx?file=src%2Froutes%2F__root.tsx

Steps to Reproduce the Bug or Issue

  1. Create routes/very/deep/path.tsx
  2. Render a link such as
    <Link from="/very/deep" to="path" />
  3. from is not allowed by the types
  4. Runtime error:

    Invariant failed: Could not find match for from: /very/deep

Expected behavior

Arbitrary from paths should be allowed as long as they're valid.

Screenshots or Videos

No response

Platform

  • OS: Win11
  • Browser: Chrome
  • Version: 131.0.6778.140

Additional context

No response

@schiller-manuel
Copy link
Contributor

I don't understand the problem here. if the route does not exist, why should it be valid value?

@nstepien
Copy link
Contributor Author

I have identical routes between two different parents

/* support staff have access to these routes */
/enterprise/$enterpriseId/Admin/...
/* end users have access to these routes */
/Admin/...

Nearly all routes under these /Admin/ paths are identical pages, so I need to use relative paths throughout.
As such, routes render components like this:

function RouteComponent() {
  return <Page from={Route.to} />;
}

This works, but the from needs to be passed all the way down.

I thought about making a hook to avoid passing down from prop:

const adminFrom = '/Admin';
const enterpriseAdminFrom = '/enterprise/$enterpriseId/Admin';

export function useAdminFrom() {
  const matchRoute = useMatchRoute();

  if (isStaff) {
    if (matchRoute({ to: enterpriseAdminFrom, fuzzy: true }) !== false) {
      return enterpriseAdminFrom;
    }
  } else if (matchRoute({ to: adminFrom, fuzzy: true }) !== false) {
    return adminFrom;
  }

  throw new Error('Do not use useAdminFrom outside admin routes');
}

but because /Admin does not have a Route, I get runtime and TS errors.

I could use absolute paths, but I'd have to handle both staff and user links everywhere:

const enterpriseId = /* get id from route */

assert(isStaff && enterpriseId);

const link = isStaff
  ? linkOptions({ to: '/enterprise/$enterpriseId/Admin/path', params={{ enterpriseId }} })
  : linkOptions({ to: '/Admin/path' });

I'm curious to know what the reasoning is behind not allowing
<Link from="/very/deep" to="path" /> or <Link from="/enterprise/$enterpriseId/Admin" to="relative" />
From my point of view I'm just building a relative link, without having to pass the params of the from.

@schiller-manuel
Copy link
Contributor

can you please enhance your example to reflect your actual use case more closely?

@nstepien
Copy link
Contributor Author

Here's an enhanced example: https://stackblitz.com/edit/github-9rvubqfj?file=src%2FAdmin%2Fusers%2Flist.tsx

Try replacing UsersList with the UsersList2 implementation and you'll get runtime/type errors.
I can't think of a better way to implement the use case where the same page may be rendered under different paths.

@schiller-manuel
Copy link
Contributor

Here is a possible solution: https://stackblitz.com/edit/github-9rvubqfj-yejwxqa4?file=src%2Fhooks%2FuseAdminFrom.ts,src%2FAdmin%2Fusers%2Flist.tsx

I'm curious to know what the reasoning is behind not allowing
<Link from="/very/deep" to="path" /> or <Link from="/enterprise/$enterpriseId/Admin" to="relative" />
From my point of view I'm just building a relative link, without having to pass the params of the from.

The restriction of the current implementation is that from must be an existing route. Only then the relative path might be constructed. It's not just checking whether the resulting path is valid.

@nstepien
Copy link
Contributor Author

Here is a possible solution

Is it safe if the enterpriseId param isn't set?
I hadn't realized params of a to could be safely omitted if they're already present 🤔
I'll try your approach, thank you very much!

@schiller-manuel
Copy link
Contributor

Is it safe if the enterpriseId param isn't set?

please go in more detail which scenario you mean

@nstepien
Copy link
Contributor Author

The exact scenario in your solution:

  const to = `${from}/users/$userId` as const;
  return (
    <Link to={to} params={{ userId: '123' }}>

@schiller-manuel
Copy link
Contributor

In this particular scenario, yes. If you are currently on a route that has $enterpriseId and navigating to a route that also has it, the value will be carried over unless specified in params.

@schiller-manuel schiller-manuel added the types Changes to the typescript types label Dec 15, 2024
@nstepien
Copy link
Contributor Author

<Link from={`${from}/...`} />
<Link to={`${from}/...`} />

These both work well, but I lose autocompletion when typing the path. I guess it's a technical limitation of TS.
I hope route-less from will be supported someday. 🤞

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

No branches or pull requests

2 participants