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

How to use History API State with hash urls? #435

Closed
matthewrobb opened this issue Feb 20, 2017 · 17 comments
Closed

How to use History API State with hash urls? #435

matthewrobb opened this issue Feb 20, 2017 · 17 comments

Comments

@matthewrobb
Copy link

matthewrobb commented Feb 20, 2017

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?

@mjackson
Copy link
Member

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.

@matthewrobb
Copy link
Author

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.

@mjackson
Copy link
Member

mjackson commented Feb 20, 2017

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.

@matthewrobb
Copy link
Author

@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?

@mjackson
Copy link
Member

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.

@mjackson
Copy link
Member

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.

@matthewrobb
Copy link
Author

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.

@mjackson
Copy link
Member

Mine too! ;)

@matthewrobb
Copy link
Author

@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.

@matthewrobb
Copy link
Author

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

@matthewrobb
Copy link
Author

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.

@mjackson
Copy link
Member

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.

@matthewrobb
Copy link
Author

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

@mjackson
Copy link
Member

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.

@matthewrobb
Copy link
Author

@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.

@matthewrobb
Copy link
Author

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

@matthewrobb
Copy link
Author

@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.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants