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

Linking "up" using relative paths #8086

Closed
mjackson opened this issue Sep 29, 2021 · 3 comments
Closed

Linking "up" using relative paths #8086

mjackson opened this issue Sep 29, 2021 · 3 comments
Labels

Comments

@mjackson
Copy link
Member

mjackson commented Sep 29, 2021

We need a reliable way to "link up" to parent routes from child routes.

The Problem

Currently (beta.5) <Link to> values that contain .. segments operate on URL pathnames. This is fairly predictable when dealing with routes that have a finite number of URL segments, but it's a problem in splat routes where the * may match an an indefinite number.

For example, in the following scenario:

<Routes>
  <Route path="files">
    <Route path="*" element={<Link to=".." />} />
  </Route>
</Routes>

The <a href> rendered by the <Link> will change depending on the URL pathname, stripping off just the last segment of the URL each time.

/files/cat.jpg                         <a href="/files">
/files/pics/cat.jpg                    <a href="/files/pics">
/files/pics/cats/cat.jpg               <a href="/files/pics/cats">

So if you want to link back to the files URL you have to either:

  • use an absolute <Link to="/files/..."> (which breaks nesting under parent <Routes>) or
  • count the number of URL segments in params["*"] and use the same number of .. segments in your relative <Link to>

Neither solution seems ideal.

Proposal

Instead of having leading .. segments traverse "up" the URL pathname, we could have them traverse "up one route". So, in the scenario from up above, the anchor hrefs would now be consistent.

/files/cat.jpg                         <a href="/files">
/files/pics/cat.jpg                    <a href="/files">
/files/pics/cats/cat.jpg               <a href="/files">

The .. segment is shorthand for "go up one route". Similarly, <Link to="../../pics"> would go up two routes and build on the route pathname there. This is a huge win for "linking up" using relative links.

This is also consistent with other relative links that do not use ... They resolve relative to the route pathname, not the current location. These are a little bit easier to think about since they are "linking down", but the principle is the same: links resolve relative to routes.

The main trade-off here would be that .. would no longer work like it does in a normal <a> element, which is going to be confusing for a lot of people.

However, I believe this is an issue only with routes that define more than one segment like some/:id-style routes. So for example, if these were your routes:

<Routes>
  <Route path="inbox">
    <Route path="messages/:id" element={<Link to=".." />} />
  </Route>
</Routes>

That <Link> would render <a href="/inbox">, which may be unexpected since you already intuitively know the number of segments in the URL pathname.

In this case, if you wanted to link to a different message (sibling route) you would use <Link to="../messages/123">. So your links would have to know something about the route path they are defined in.

This feels like the kind of thing that users would hit once or twice, learn, and then be able to adapt to it.

In addition, we already don't follow the semantics of <a href=".."> when it comes to trailing slashes, so having separate semantics for <Link to> is something that React Router users should already be familiar with.

@mjackson mjackson changed the title [Bug]: <Link to=".."> should build upon parent route path, not URL pathname Linking "up" using relative paths Oct 7, 2021
@andycarrell
Copy link

🙋 Will the functionality of "linking up" to parent routes apply to Navigate too?

We use <Navigate to=".." /> to catch unknown paths and redirect to a sensible default:

<Routes>
	{/* ... other route definitions */}
	<Route path="*" element={<Navigate to=".." />} />
	<Route path="" element={<Navigate to={defaultSlug} />} />
</Routes>

@skoging
Copy link

skoging commented Oct 12, 2021

🙋 Will the functionality of "linking up" to parent routes apply to Navigate too?

We use <Navigate to=".." /> to catch unknown paths and redirect to a sensible default:

<Routes>
	{/* ... other route definitions */}
	<Route path="*" element={<Navigate to=".." />} />
	<Route path="" element={<Navigate to={defaultSlug} />} />
</Routes>

Based on my interpretation this would work, even if path="*" match something like "/some/path/with/many/segments".

<Navigate to=".." /> should simply navigate "up" from whatever was matched by the closest parent route: <Route path="*".

I think this is a very good approach to this issue, I've had to get quite creative in working around this in the past, and look forward to finally deleting that code.

It would be a breaking change when it came to messages/:id-style routes, but I expect this alternative would work around that:

<Routes>
  <Route path="inbox">
    <Route path="messages">
      <Route path=":id" element={<Link to=".." />} />
    </Route>
  </Route>
</Routes>

mjackson added a commit that referenced this issue Oct 14, 2021
mjackson added a commit that referenced this issue Oct 14, 2021
@mjackson
Copy link
Member Author

This was fixed in 0d22590 and will be released in beta.7

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

3 participants