router
router copied to clipboard
The `Link` component doesn't generate hashed links with hash history.
Describe the bug
when creating my app with hash history instead of browser history I would expect a rendered Link
component to href to #/about
for example but instead see it render href to /about
.
Interestingly when I click on it it does render and set my browser location to #/about
, so something seems to be correcting it post-click, but if I right-click to open in a new tab on that same link it opens at the /about
href URL and breaks the hash route.
My router setup is basically the canonical tan router example app with hash history swapped in:
const history = createHashHistory();
const router = createReactRouter({ routeConfig, history })
This looks to be similar to (the same as?) https://github.com/TanStack/location/issues/300 but I am indeed running the latest version of tanstack router.
Your Example Website or App
https://stackblitz.com/edit/github-jssymk?file=src/main.tsx
Steps to Reproduce the Bug or Issue
- Visit a page with a
Link
component. - Hover over link and note the URL it directs to.
- Right click the link and open to a new tab, noting the browsers location.
- click the link and not the the browsers location.
Expected behavior
At step 2 of reproduction I would expect step to to show a hashed url, for eg <Link to="/about">
should yield #/about
but instead I see /about
.
At step 3 of reproduction I would expect the tab to open at #/about
, but instead it opens at /about
.
At step 4 of reproduction I expect the browser location to reflect the broken not-hashed url but weirdly it shows the correct hashed URL.
Screenshots or Videos
Platform
- OS: Linux
- Browser: observed on Chrome and Firefox, likely the same on other browsers
- Version: 0.0.1-beta.38
Additional context
No response
To add to this report:
- I have this issue when using Github Pages with React Location. Github pages does not allow rerouting traffic to the index.html, so the hashurls are the only way to go (or some obscure 404.html workaround).
- This issue gets more confusing once your deployed code is accessed in a sub-directory like GH Pages often is (eg https://osmberlin.github.io/osm-tag-updater/)
- I worked around this issue for one link at https://github.com/osmberlin/osm-tag-updater/commit/9a18f40fef3cced7317d7345eb6aa0b3e83d8262#diff-59e03d506cb04167415dc0dc03fa3d4e803c88d666f29b2773579d9dbbed87ffR48-R55 by using
navigate
together withto
which does feel hacky - That workaround breaks shift+click (new window) and option+click (new tab). The only way to open a new tab/window is via the right mouseclick menu. Therefore …
A good test case for a fix would be…
- works with shift+click (new window)
- works with cmd+click (new tab)
- works with context menu "open in new tab"
- works with context menu "open in new window"
I looked over onClick handler and it seems that ctrl-click is treated differently. In fact handleClick all it does is use navigate(). https://github.com/TanStack/router/blob/0575b2fca45e9aa73b1922b4d6b99a38e7218e66/packages/router-core/src/router.ts#L1322
@mfaradzheva-hubble the issue occurs before onclick fires.
at the point that the link is rendered onto the page, but before a click event, it is already wrong, you can see this when you hover the link.
In my screenshot you can see it as localhost:3001/about
before it is clicked, when it should show as localhost:3001/#/about
if it were generated correctly.
hi @tannerlinsley, i saw you closed this issue but i believe it's still occurring in the latest version. you can replicate by adding hash history to any of the existing examples
A good test case for a fix would be…
* works with shift+click (new window) * works with cmd+click (new tab) * works with context menu "open in new tab" * works with context menu "open in new window"
Also add middle-clicking on a link to that list, that doesn't work either right now (probably gets fixed alongside with the context-menu fixes as those probably work the same in the browser)
I can also confirm that this is an issue still for me on v1.4.6 (the latest at the time of writing this)
If the team needs a minimal reproducible example... this was with React Router v1.5.4
https://stackblitz.com/edit/stackblitz-starters-m1udae?file=src%2Findex.tsx
I still think it's this line of code that prevents the use of hash. https://github.com/TanStack/router/blob/0575b2fca45e9aa73b1922b4d6b99a38e7218e66/packages/router-core/src/router.ts#L1325
Function navigate is not executed if !isCtrlEvent(e).
const handleClick = (e: MouseEvent) => { if ( !disabled && !isCtrlEvent(e) && !e.defaultPrevented && (!target || target === '_self') && e.button === 0 ) { e.preventDefault() if (pathIsEqual && !search && !hash) { router.invalidateRoute(nextOpts) } navigate(nextOpts) } }
function isCtrlEvent(e: MouseEvent) { return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) }
I might be wrong. I don't have time to set environment up to test it. But by reading the code it seems like the issue.
I don't think the location of the links when hovering over them should be without the hash. I think you'll need to run buildLocation (or whatever function creates the actual url that is used after parseLocation with the hash) earlier so that the same url is passed to the actual link dom element. Right now it seems to update to the correct value onclick only
a tag had the correct href attribute in the DOM before this commit
reverting the change in packages/react-router/src/router.ts
to
href: this.history.createHref(`${pathname}${searchStr}${hashStr}`)
seems to cause other issues like adding additional /#/
in front of the hash, but the href attribute in the DOM is correct.
@tannerlinsley any ideas?
This issue still exists as of version 1.16.6
I have fixed it temporarily for myself using the following patch. It works for me because I know I will always use the hash router. But it may not be suitable for others.
diff --git a/dist/esm/link.js b/dist/esm/link.js
index b9b64766dabec3ac2fd505fedfddd330c6eccd35..fee51513e8d6cf147712f7223a51da785d49a184 100644
--- a/dist/esm/link.js
+++ b/dist/esm/link.js
@@ -143,7 +143,7 @@ function useLinkProps(options) {
...resolvedActiveProps,
...resolvedInactiveProps,
...rest,
- href: disabled ? void 0 : next.maskedLocation ? next.maskedLocation.href : next.href,
+ href: '#'+(disabled ? void 0 : next.maskedLocation ? next.maskedLocation.href : next.href),
onClick: composeHandlers([onClick, handleClick]),
onFocus: composeHandlers([onFocus, handleFocus]),
onMouseEnter: composeHandlers([onMouseEnter, handleEnter]),
@pSnehanshu perhaps you should wrap it in history.createHref
🤔
This has been fixed and released. You can get these changes on version 1.26.4.