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

Add history.navigate API #472

Closed
wants to merge 2 commits into from
Closed

Add history.navigate API #472

wants to merge 2 commits into from

Conversation

pshrmn
Copy link
Contributor

@pshrmn pshrmn commented Apr 29, 2017

This is a proof of concept related to #470. This PR adds a navigate method to each history type.

This is inspired by the WHATWG browser spec

If the navigation was initiated with a URL that equals the browsing context's active document's URL

  1. Replace the current entry with a new entry representing the new resource and its Document object, related state, and the default scroll restoration mode of "auto".
  2. Traverse the history to the new entry.

The basic chain of specs I used to find this started with the <a> spec, which leads to the description of how to follow hyperlinks, which in turn leads to the description of how browsers should navigate.

history.navigate(path, state)

The navigate method has the same argument signature as push and replace.

When history.navigate is called, it creates a location object and compares it to the current location object. The only fields that are compared are: pathname, search, and hash (essentially comparing the serialized version of each location).

If the next location and the current location are the same, then a REPLACE action will occur. If they are different, then a PUSH action will happen (the actual implementation varies by history type).

// when the current location = { pathname: '/fruit', search: '', hash: '#berries' }
history.navigate('/fruit#berries') // REPLACE
history.navigate('/fruit#citrus') // PUSH
history.navigate('/vegetables') // PUSH

Implementation

I added createPushCallback and createReplaceCallback functions inside each of the history types. These return the callback functions that were being passed to transitionManager.confirmTransitionTo. This approach was a lot easier than trying to play Frankenstein and putting together a monster of a callback inside of navigate.

push and replace always use their respective functions. navigate determines which one to use based on if the next location is the same as the current location.

History Dependents (specifically, React Router)

With React Router, this would allow the <Link> to use navigate instead of push. Then, the rendered <a> will have the same behavior as a regular <a>. Potentially a push prop would need to be added to <Link> so that if a user really wants to push the same location, they could, but I'm not sure why that would be.

<Link to='/somewhere'>...</Link> // use history.navigate
<Link replace to='/still-here'>...</Link> // use history.replace
<Link push to='/go-there'>...</Link> // use history.push

@@ -59,3 +59,8 @@ export const locationsAreEqual = (a, b) =>
a.hash === b.hash &&
a.key === b.key &&
valueEqual(a.state, b.state)

export const locationPathsAreEqual = (a, b) =>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Names are hard 🤷‍♂️

@pshrmn pshrmn closed this Jun 27, 2017
@pshrmn
Copy link
Contributor Author

pshrmn commented Jun 27, 2017

Closing this since it is stale. I ended up writing my own history-esque package (inspired by this and the history from vue-router) for Curi that implements this functionality and it works well. It is nice because if you navigate to the same page that you are currently on, it doesn't wipe out "future history".

@mjackson
Copy link
Member

@mjackson
Copy link
Member

@pshrmn Do you have a link to your work? I'd like to check it out. Thanks 💕

@pshrmn
Copy link
Contributor Author

pshrmn commented Jun 28, 2017

https://github.com/pshrmn/hickory (an amalgamation of history and curi)

Most everything should be familiar to you. I changed some property names, but the but API is nearly the same. Biggest differences are that I use a different blocking system, I went HTML5 only, and (the main reason that I wrote a new package) I switched to location.query from search and added built-in parsing/stringifying (it just uses strings if the user does not provide their own). Perhaps the most jarring change is that there are semicolons everywhere 😉.

It actually mostly works with React Router if you assign some property names that RR expects. https://codesandbox.io/s/gpg6lLo6 I wouldn't recommend that to anybody, just tested it out of curiosity.

Anyways, I'd be happy to give insight if you have any questions.

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

Successfully merging this pull request may close these issues.

2 participants