remix-run / react-router

Declarative routing for React
https://reactrouter.com
MIT License
52.79k stars 10.23k forks source link

Relative <Link to> #2172

Closed Robinfr closed 8 years ago

Robinfr commented 8 years ago

Right now it seems that the Link component is always absolute. It would be nice that if the to URL does not start with a / that it becomes relative to the current route.

E.g. current route is: http://localhost/#/zoo/

<Link to="giraffe">Giraffe</Link>

Clicking on the link should bring me to http://localhost/#/zoo/giraffe/ instead of http://localhost/#/giraffe/

taion commented 8 years ago

Took a quick look – YSK that once we get things merged, you ought to be able to do named routes as a history enhancer. As it stands, I don't think we want to introduce new React elements for 2.x – we just want to get this out the door. Maybe 2.1.x.

ericclemmons commented 8 years ago

Sorry, there's no code there yet :) I'll try to get a PR going soon & tag you separately so I no longer make noise in this channel. For it to work, every route-level component needs to have .route introduced again, so this is probably not best for 2.0, unless that change is un-deprecated :D

taion commented 8 years ago

Motivating example:

<Route path=":org" component={Org}>
  <Route path=":repo" component={Repo}>
    <IndexRoute component={RepoIndex} />
    <Route path="issues">
      <IndexRoute component={IssueList} />
      <Route path=":issueId" component={Issue} />
    </Route>
  </Route>
</Route>

The only way a user can usefully make relative <Link>s in <Org> and <Repo> is if they're relative to the route context, rather than to the current document.

If I have <Link to="issues"> in <Repo>, then this needs to do the same thing whether I'm on reactjs/react-router, on reactjs/react-router/issues, or on reactjs/react-router/issues/2172.

Otherwise it's not really useful.

taion commented 8 years ago

From https://github.com/mjackson/history/issues/240:

One of the big challenges is that relative link support requires the router to be able "handle" the link, e.g. in the <Link to="issues"> above because only the router can know where that link should go.

On the flip side, we get a lot of power out of to just being an opaque location descriptor that history can consume.

I can't think of a good resolution. It's possible that relative links need to be in an add-on library that makes specific assumptions about the history, rather than in React Router core.

frankwallis commented 8 years ago

Maybe I'm missing something, but can't Link just be given its root path by Route using React context, and then construct the absolute path?

taion commented 8 years ago

That breaks the nice property of having to just be an opaque location descriptor.

frankwallis commented 8 years ago

I don't really understand what is meant here by 'opaque' - in this context isn't it the same as 'absolute'? It may be a nice property to have but it doesn't support some very common use-cases.

There could be a separate component RelativeLink but I don't think it's going to be possible without support from Route.

taion commented 8 years ago

The idea is that I should be able to do:

const history = useRouterHistory(useNamedRoutes(createBrowserHistory))({ routes })

And then have things like

<Link to="route-name" />

And having the router assume that string location descriptors are paths would make this impossible.

frankwallis commented 8 years ago

Thanks for the explanation, I didn't know about the named routes. Perhaps then relative paths should always start with ./ or ../ similar to how they work in nodejs?

ericclemmons commented 8 years ago

I've been using relative routes myself which seem to work with ../ and stuff, like you normally would in an <a href="...">: <Link to="../docs" />.

The problem with named routes is that the async nature of getChildRoutes() means you're never fully aware of the full route table.

Solving named routes, IMO, is the best solution, but I'm just an end-user of this lib. (FWIW, a full list of URLs for a complex app doesn't seem to be too costly, as long as you code-split all of the deps :D)

ryanflorence commented 8 years ago

We're bringing this to the router, work has started over here and will be brought into core when the kinks are worked out over there.

frontsideair commented 8 years ago

This just bit me today. I gave a relative address to Link element without noticing and wondered what was wrong when it rendered the wildcard route. Curiously, it rendered the correct route when I used browser back button and then went forward.

taion commented 8 years ago

As part of this, we will want to put route on context.router.

taion commented 8 years ago

I am dropping this from the v3 milestone – while I very much think we should have this feature, I don't think we can treat this as a v3 blocker.

firasdib commented 8 years ago

I've read through this entire issue and I don't see what the problem is. Now I have to use something like https://www.npmjs.com/package/resolve-url and feed it resovleUrl(window.location.pathname, '../') which feels like something that should be natively supported. Could someone ELI5?

Robinfr commented 8 years ago

I must say guys, even though I don't entirely agree with not following how a normal link works, I think amazing work is being done!

shaibam commented 8 years ago

So... is there gonna be a way to pass a relative link in the 'to' field or not?

taion commented 8 years ago

This is a feature that we want to support. I'm not aware that anybody is actively working on it, though.

optimatex commented 8 years ago

I'm sorry guys but after reading i do not understand what is already done and what is in plans. the variant <Link to="../docs" /> doesn't work for me. As i understand the only way to realize relative link is <Link to={${this.context.location.pathname}/picture/${id}} > - at least it works for me

txm commented 8 years ago

This is my "solution" :/

function splitURL(href) { return href.split('?')[0].split('#')[1]; }

let url = splitURL(window.location.href) + '/' + this.props.value;

just-boris commented 8 years ago

@txm look at solution by @optimatex, it is better because uses only react-router internals, and doesn't rely on global window object

timdorr commented 8 years ago

Works in v4 :) https://react-router-website-uxmsaeusnn.now.sh/recursive-paths

Blackclaws commented 7 years ago

Scratch my complaint. I was still under the impression that the resolution was workable for v2/v3 at some point.

All in all does this mean that it won't be supported in the old v2/v3 branch ever but will only be available using the v4 Match syntax?

saiichihashimoto commented 7 years ago

I was under the assumption that v2/v3 was to be supported

mjackson commented 7 years ago

@saiichihashimoto v3 will receive bugfixes but no new features. Part of adding this particular feature meant that the code needed to be refactored in backwards-incompatible ways.

adamdonahue commented 7 years ago

Realize this is old, but is there a solution to this yet?

Assume a path like:

/deals/:dealId/trades/:tradeId/notes/:noteId.

Further assume we're using tabs at each level.

In order to link from one note to another, then, I need that component to know the context in which it exists.

<Link to={`/deals/${dealId}/trades/${tradeId}/notes/${noteId}`}>Note 1 Tab</Link>

and so forth. So a note needs to know its context when in fact it shouldn't. Is there an elegant pattern around this?

timdorr commented 7 years ago

See #5127