History: Hash history clobbers existing non-hash URL path

Created on 9 Jun 2017  路  13Comments  路  Source: ReactTraining/history

Hi all, I'm using createHashHistory() to manage routing for a set of forms within an existing web application. The forms are hosted within a nested path in the application, for the sake of example let's say they're at /path/to/forms. When the page loads, the hash history is rewriting the path to /, clobbering the browser history, but leaving the browser on the same page. Refreshing the page of course causes the home page to load. I don't want this happening as users may bookmark some of the form pages, and they will only load the home page when they come back to them later.

I've tried setting basename in the options, but with no success (I don't believe that basename solves this use case, anyway). Is there an existing fix for this?

Most helpful comment

Are you using <base> tag in your page? <base href="/"> leads to the results you described.

All 13 comments

Can you list the actual URIs (the pathname/hash portion) to be a little bit more exact with your description? I'm having trouble understanding exactly what is happening.

| action | pathname |
|---------|----------------|
| load page | /path/to/forms |
| hash history sets hash | /path/to/forms/#/ |
| refresh | ??? |

I'm pretty sure what I've outline is incorrect.

|action|pathname|logical page|
|------|---------|------------|
|load page|/path/to/forms|main forms page|
|hash history sets hash|/#/|still main forms page|
|refresh|/#/|home page|

Hmm, that's weird. For a hash history, it shouldn't touch the actual pathname, it should only manipulate the portion after the #. Interaction with the window.location is done here and here with the latter being the only one that actually touches the href. When there is no existing hash, it should just add to the end of the existing pathname.

You don't have a browser history instance or some other URI manipulation that could be causing the redirect to your root?

Also, you are correct that the basename should not be used here.

I am using react-router-redux. Is it possible there needs to be some initial state in there to set the base path?

I've removed all dependencies on redux and the problem persists. I'll try to narrow down what's causing it.

Looks like initialLocation is getting set to /:
https://github.com/ReactTraining/history/blob/v4.6.1/modules/createHashHistory.js#L171
https://github.com/ReactTraining/history/blob/v4.6.1/modules/createHashHistory.js#L75

The function getDOMLocation() doesn't take into account the existing path the browser is at. Only the hash path is handled, and if it's empty, it's set to /.

initialLocation should be / when the original URI does not have a hash. The pathname of a hash history location object only describes the portion of the URI that comes after the pound sign.

| URI | hash location pathname |
|-|-|
| /path/to/forms | / |
| /path/to/forms#/test | /test |

So it looks like that^^ is expected. Here's where I think the problem actually lies:
https://github.com/ReactTraining/history/blob/v4.6.1/modules/createHashHistory.js#L53

const replaceHashPath = (path) => {
  const hashIndex = window.location.href.indexOf('#')

  window.location.replace(
    window.location.href.slice(0, hashIndex >= 0 ? hashIndex : 0) + '#' + path
  )
}

Because hashIndex is zero (there's no hash at /path/to/forms) the window's location is replaced by only #/. I think that call to slice() needs to be fixed up a bit.

That should not be an issue either. When there is no pound sign, then the result of slice will be an empty string, so you are effectively calling window.location.replace('#' + path). That _should_ just append the hash to the end of the current URI.

I've tested/confirmed that is the case on Chrome and Firefox (only browsers I have on this computer) by just calling window.location.replace('#test'). I also have a page that uses hash history and doesn't get rid of the pathname prior to the pound sign when the app loads.

Have you been stepping through your code in the debugger to see when the URI changes? I really want to say that it is something other than hash history which is nuking the pathname.

Are you using <base> tag in your page? <base href="/"> leads to the results you described.

I'm not using <base> tag. I've also been stepping through the code and it's definitely happening right here. I'm using Chrome 58.0.3029.110.

I spent some time over the weekend fiddling around with the setup and noticed I was using versions [email protected] and [email protected] pulling in [email protected]. There's no pairing of react-router-redux and react-router for version 4.1.1. I pulled in [email protected] and the problem went away. Awful frustrating.

Anyway, I think I'm set, thanks guys for helping out :)

Hmm, sounds like it was an issue with react-router-redux. FWIW, react-router-redux 5.x is the first version that works with react-router 4.x. It's a bit confusing, but react-router-redux was already at 4.x when we shipped react-router 4.x, so that's why.

I am seeing this issue and am not using react-router-redux.

My app is reloading on load after being proxied through a different webserver, to get around drag and drop issues on different domains on Chrome.

  • Served from http://foo
  • react-router calls window.location.replace("#/)
  • Now, served from http://bar

Using v3.2.0. Wondering if a migration to v4 would fix it.

@pshrmn I am not seeing the behaviour you describe in either Chrome or FF. Calling window.location.replace("#foo") causes the page to reload.

EDIT: I've tried upgrading to v4. It did not solve the problem.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

wssgcg1213 picture wssgcg1213  路  6Comments

sidoruk-sv picture sidoruk-sv  路  3Comments

AndreasHogstrom picture AndreasHogstrom  路  4Comments

richardgorman picture richardgorman  路  5Comments

yangmingshan picture yangmingshan  路  6Comments