History: How to use History API State with hash urls?

Created on 20 Feb 2017  路  17Comments  路  Source: ReactTraining/history

I have a number of reasons why I am limited to using the hash potion of the url for my dynamic application routing however I am not limited in a way that requires I support older browsers that do not have HTML5 History API available. I'd like to be able to store state objects in navigation while using hash urls, is this possible?

EDIT: I guess another way to ask this would be is it possible to route on the hash using the html5 history api?

Most helpful comment

Honestly I am sort of dumbfounded that it seems to be working without anything more than:

<BrowserRouter basename={`${location.pathname}#`}>
...
</BrowserRouter>

I'm not doing anything super fancy other than storing modal clicks in location state.

All 17 comments

So you can't change your server to support a history-enabled frontend?

No, we don't currently support navigation using the hash and using pushState for the location changes. I guess technically we could do it, but it seems like such an edge case. Most people want one or the other.

I am building an app in an environment that doesn't easily allow me to control server side routing but I'd like to support deep linking features so I can't really use normal path routing due to not being able to wildcard route to my app front end :/ Is this something I could do? Some direction would be appreciated and if it's straight forward enough I will see if it can come back as a PR.

Well, currently the concepts of how a route is stored in the address bar and managing when it changes are fairly tightly coupled.

Browser history assumes:

  • You're using window.location.pathname to store the URL
  • You're using popstate to listen for when it changes, which supports location-specific state

Hash history assumes:

  • You're using window.location.hash to store the URL
  • You're using hashchange to listen for changes, which doesn't support location-specific state

If you wanted to change this coupling, you'd have to make stuff a little more pluggable. For example, you could have a "location management strategy". This would likely be an object that you could plug in to either createBrowserHistory or createHashHistory to tell it how to persist and get changes to the current location.

So, for example, a "browser location management strategy" (we can bikeshed on the name) would use pushState, replaceState, and popstate to manage the location. Then, if you wanted to use it with hash history you could maybe do something like:

const history = createHashHistory({
  strategy: browserLocationManagementStrategy
})

In this scenario the terms "browser history" and "hash history" would refer only to what the URL looks like, not how it is stored.

@mjackson Would it not be possible/simpler to have a param you can pass to browserHistory similar to hashType that would allow you to just prepend a hash to the url passed to pushState?

history.pushState({}, null, `#${path}`);

This is probably more relevant in the context of React Router (which is where I want this functionality) but is that even doable? Do all the normal events fire etc?

Yes, I think that might work. We currently handle the hashchange event in browser history, and even support it in browsers that don't fire popstate on hashchange.

In that case you'd just need to modify browser history to store everything in the hash portion of the URL. I'm not sure if this would mean that we would not be able to support location.hash. Also, you'll lose the browser's built-in ability to automatically scroll to the element with the hash id.

Hi @matthewrobb, anything to do here? Would you like to send a PR? Or did you just want to ask about possible ways around this? I don't really have anything more to add.

Hey, sorry you can close this, I won't have a PR for this in the near term. It's in my mental 'ice box' though for sure.

Mine too! ;)

@mjackson I was playing around with this for about 2 minutes and the only thing I did was this: (in createBrowserHistory

  const createHref = (location) =>
    '#' + basename + createPath(location)

and my entire app continues to function as it did before (using normal Browser History). Can you think of any major gotchas to this strategy?

To give you a bit more context about my limitations: I have to build an app that lives in Sharepoint Online (Office 365) and my ability to do any sort of dynamic routing server side is pretty much zero.

Also it seems to work if I just set basename="#".

Honestly I am sort of dumbfounded that it seems to be working without anything more than:

<BrowserRouter basename={`${location.pathname}#`}>
...
</BrowserRouter>

I'm not doing anything super fancy other than storing modal clicks in location state.

Huh, ya I guess that'd work. I tried to make basename as generic as possible so you could put literally anything you want in there.

I'm just curious if this will have adverse/unexpected side effects coming from browser routers support for hashchanged event etc?

I don't know. Currently we only add support for hashchange for browsers that don't fire popstate when the hash changes. Looks like IE10 and IE11 do not.

@mjackson Not sure if something changed and if so where (been testing in Chrome). It seems like before using the method described above that it would work to change the hash portion of the url in the url bar of my browser. Now it seems to not be firing... WAIT.

Nevermind, I had a PureComponent in the way !!!! This is still working well for me!

@mjackson Just want to keep noting any details around this use case that come up as I play with the various options. There ARE some edge issues with:

<BrowserRouter basename={`${basename}#`}>
...
</BrowserRouter>

It works well but you do lose a few things like what you have mentioned. Another option I recently came up with and that I think makes MORE sense for my use case is this:

<BrowserRouter basename={`${basename}?`}>
...
</BrowserRouter>

You have to give something up with either method but they both enable you to route using history and state when you can't control the server routing.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Carduelis picture Carduelis  路  6Comments

clayrisser picture clayrisser  路  4Comments

vass-david picture vass-david  路  8Comments

zhangdingding picture zhangdingding  路  4Comments

mereskin-zz picture mereskin-zz  路  4Comments