-
Notifications
You must be signed in to change notification settings - Fork 960
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
Accept objects in push and replace #141
Conversation
The immediate benefit of this will be to enable patterns like history.push({ ...location, query: nextQuery }) |
6eca7c7
to
563b9eb
Compare
Squashed in a couple of test cases showing off the using spreads. I'm aware this doesn't update the changelog - it takes me a long time to write that sort of thing, so wanted to get feedback on the new functionality before adding a changelog entry. |
Ah - this is a little tricky because of our semantics with handling |
Well - this resolves it. I don't love this implementation, but I don't hate it either. For a breaking change, it'd be nicer to just drop the In practice, though, I think this implementation is good enough. |
08a16ab
to
32484d0
Compare
I have a local branch that implements this as well; just haven't had time to push it yet. I'll take a look at this on my flight to the UK this weekend. Thanks for all your work here, @taion. :) |
A couple of things to note - I'd like to resolve this TODO sooner rather than later, but it'd require a (mostly non-breaking) 2.x. Should hold off on actually doing a major version bump to aggregate a few more of these technically breaking changes, though. Also, my real motivation here isn't just that this API feels "cleaner" in some sense. Concrete things:
|
cc @jlongster This also opens up the path toward adding a more generic |
On second thought, that might be too generic an API. Probably better for integrations that want to do this to just implement both push and replace hooks. |
The work required for #162 is going to touch a lot of these same points. I think it may be a bit cleaner to do that first. |
I disagree - I don't think #162 is on-path. The longer-term goal here is to offer a "generic" API to push and replace that better supports paradigms outside of the state/query/path paradigm, which is very focused toward doing path-based routing in browser. One of the goals here is to move the React Router <Link to={{path: '/foo', query: {the: 'query'}, state: {the: 'state'}}> Where the arguments to |
This is appealing. |
if (location.query == null) { | ||
const { search } = location | ||
location.query = parseQueryString(search.substring(1)) | ||
location[SEARCH_BASE_KEY] = { search, searchBase: '' } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've read the code through a few times, but I'm still not sure what searchBase
is or why it's necessary. Is there any way you can make this code more clear?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's for https://github.com/rackt/history/pull/141/files#diff-822f74db4c614a797843213f3d8ad044R71.
Suppose you do history.push({...location, query: nextQuery})
. Because useQueries
currently appends the queries to the existing search, this will append the contents of the query to the old search.
I don't expect this to be what anyone wants. The ideal solution here is to change the useQueries
semantics to overwrite search
entirely, but I don't want to make a breaking change yet.
This is a bit of a hacky workaround - just check to see what the "intended" search is. It's ugly, but I couldn't figure out anything better that was non-breaking. I think this will work well enough for most practical use cases, and would rather ship this than hold off on a 2.x release for this change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the test case you linked to, the given query overwrites the pre-existing query string instead of appending to it. Am I missing something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The point of this logic is exactly to allow that overwriting (not exactly overwriting but close).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The $searchBase
is quite a bit of a hack. The way this works is that if you have a user-pushed location that looks like e.g.
{
path: '/the/path',
search: '?a=one',
query: { the: 'query value' },
}
We end up getting producing something that looks like:
{
path: '/the/path',
search: '?a=one&the=query+value',
query: { the: 'query value' },
'$searchBase': {
searchBase: '?a=one',
search: '?a=one&the=query+value',
}
}
Suppose we want to follow the pattern above and do something like:
history.push({
...location,
query: { other: 'query value' },
})
The object that actually gets pushed looks like:
{
path: '/the/path',
search: '?a=one&the=query+value',
'$searchBase': {
searchBase: '?a=one',
search: '?a=one&the=query+value',
}
query: { other: 'query value' },
}
Since the search
matches history['$searchBase'].search
, we replace it with history['$searchBase'].searchBase
, and in a roundabout way end up with search
as ?a=one&other=query+value
, which (given the current append semantics) is what we expect.
On the other hand, if the user does something like:
history.push({
...location,
search: '?b=two',
query: { other: 'query value' },
})
Then we end up with search as ?b=two&other=query+value
.
There are edge cases here, but I expect that people are very unlikely to run into them in practice. I intend this as a hack that we remove ASAP, but one that is necessary if we don't want to break backward compat per the current "when there is already an existing search" test case.
The idea would be something like releasing this as v1.14.0 and taking a bit more time to gather stuff like the potential query-string
for qs
replacement before cutting v2.x. The TODO
below is sort of in lieu of further documentation, because I didn't want to imply that this was deservedly complicated code that we should keep - it isn't anything of the sort.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome, thanks for the detailed explanation.
I think ultimately useQueries
should probably just ignore any existing search
value and we should alter that test to make sure the existing query string is overwritten. I think this is a lot clearer in the object-based API where everything is in the same argument. I understand you'd like to keep backwards compat for now, but let's def drop in 2.x.
Accept objects in push and replace
Thanks, @taion! Let's be sure to follow up with a note in CHANGES.md. |
Ah crud, I should also have cleaned up the commits on this branch. I want to drop the hack in useQueries ASAP - entirely just a question of what else we want to break first. I'm trying to track possible things on #164... possibly at least all the things under "minor", just to get those out of the way. |
Discussed on Discord a bit, thought I'd get some code in place.
I think this is a bit of a first step toward remix-run/react-router#2186, as described in remix-run/react-router#2186 (comment).