-
-
Notifications
You must be signed in to change notification settings - Fork 10.3k
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
should history
be router API or implementation detail?
#2614
Comments
Also, @mjackson and I talked about this while traveling around in London and he's on board (with this idea, he got off the train yesterday) |
I was trying to get at this a bit with #2502. There's a slight issue with the API above, in that the <Router history={useRoutes(useQueries((createHistory))({routes})} /> This is what I meant w/r/t the "90%" API, in that I feel like most users would prefer to do <Router history={createBrowserHistory()} routes={routes} /> Like you pointed out, this means that What I meant to get at with #2502 is that, in addition to just being a cleaner API, having a That's a bit "ick", but it lets us preserve the API where the 90% use case can just pass in |
tl;dr |
BTW, the I hack around this for remix-run/history#141 but it's a bit messy and we should make a breaking change there. |
Oh, yep. Seems like we can see they sent a string "browser" or "hash" and then wrap, otherwise we leave it all alone. So 90%: import { Router } from 'react-router'
// we know to wrap because they sent a string (an old, beloved API we once had)
render(<Router history="browser" routes={routes}/>, document.getElementById('app')) 10% - folks who want their own history const history = useRoutes(useQueries((createHistory))({routes})
<Router history={history} /> |
That's one option. I don't think it's entirely ideal, because it restricts the set of "raw" histories that can be used, and means that we have to pull all of them into the React Router package (I'm unhappy enough as-is that we always pull in What I'd like to see is that if we merge #2580, we can just check for whether |
Ah, yeah this is kinda hairy, let me think on this a bit. |
My thought was to expose something like export default function wrapHistory({
history = createHashHistory(), routes, children
}) {
if (history.listenRoutes) {
return history
}
return useRoutes(() => history)({
routes: createRoutes(routes || children),
})
} It's not beautiful, but the nice thing is that e.g. redux-simple-router can use this function to make sure it has a routing history, then call EDIT: Code style |
Here's another idea <Router history={someHistory}/> If you didn't provide |
We can do that. My thought with something like <ReduxRouter history={history} store={store} routes={routes} /> If we expose that sort of |
This is a bit orthogonal, though, since we can still do #2614 (comment) and #2614 (comment). It just depends on whether we think |
Perhaps instead, you could introduce another prop to make this very explicit: <Router createHistory={createBrowserHistory} routes={routes} />
// useRoutes(useQueries(this.props.createHistory()))({ routes }) is inside of Router
// Otherwise, assume the history is ready to go.
const reduxHistory = useRoutes(useQueries((createReduxHistory(store)))({routes})
<Router history={reduxHistory} /> |
👍: Getting rid of I really like checking for But enough shilling for my own idea (: |
Been thinking about this off and on the last couple days, and discussing with @mjackson. What if coupling route config to
We already have
Now, I think we have cleaner solutions to all of those issues w/o new API, maybe even less. |
Ya, agree with @ryanflorence. |
What if we just export a Our API could just be boiled down to: import { render } from 'react-dom'
import { match, RoutingContext } from 'react-router'
history.listen(location => {
match(location, routes, (error, state) => {
render(<RoutingContext {...state} />, node)
})
}) Pro: Things like Con: More boilerplate for common use cases, but maybe we could wrap it up in |
I think you've hit the nail on the head. What's the point of having a separate history library if you're going to manipulate it so heavily in We should be able to use history's APIs without any monkey patching. If not, we need to enhance history, which benefits everyone. |
By "enhance history", I mean develop features and code in the history library, not enhance it in Router's code. |
I'm 👍👍👍 on the core idea here. That said, taking an inventory of extensions, I think we can actually keep the API almost in its entirety. The reason we can do this is because most of our extensions don't need to listen to the routing history, and it's entirely an accident that they do. To take a quick inventory: AsyncProps, react-router-relayEasy case, neither care about redux-simple-router, alt-routerThese don't need to use const routes = (
<Route component={connectToStore(store)}>
{actualRoutes}
</Route>
) That should be good enough for redux-simple-router to work as intended. redux-routerI actually have no clue how redux-router works. scroll-behaviorThe current API has scroll-behavior work as a history enhancer. It's actually similar enough to the redux-simple-router and alt-router cases above that I'd probably want to re-write scroll-behavior to work in the same way - probably just lean toward making it an abstract route like in the above code snippet, which also then might let us implement @ryanflorence's idea of managing scroll behavior for individual scroll-able components. Future native routerOne main concern here is integrating with |
BTW, I think the best way to do this would be to have something like: <Router history={history} matcher={matcher} /> The The idea would be that the This lets us keep the exact same API for users - I don't think there's a good reason to wrap
I don't know how to handle those two cases above, but I feel like the idea of making |
To flesh out my proposal a bit more, the user-facing API would look something like: static contextTypes = {history, router, location} Then you can do: this.context.history.push(location)
this.context.history.replace(location)
const isActive = this.context.router.isActive(locationSpec)
const unlisten = this.context.router.listenBeforeLeavingRoute(route, hook)
// A form where we explicitly specify the location to compare against would let
// us clean up the animation example, maybe enough to drop the inscrutable
// <StaticContainer> there.
const isActive = this.context.matcher.isActive(
locationSpec, this.context.location
) It's a slightly larger context surface area, but it has the nice property of exactly preserving the |
One more wrinkle though. It'd be nice to be able to change async I believe we don't have this right now. My understanding is that currently I don't think we can fully mimic this behavior without the router connecting deeply into the history, because we'd need to suppress I can't really think of a good way to handle this at all, but I feel like it might require some coupling between the |
Eh, I take that back, maybe we just need to do the routing from |
If this could make it feasible using react-router without history, I'm glad to see it. |
We need |
see #2646 |
Right now this is kinda weird:
So what?
useQueries
anduseRoutes
. In order to transition in an action withmyOwnHistory
you have touseQueries
yourself, causing it to be wrapped twice :(useRoutes
and now the listeners fire in unpredictable ordersWhat if
history
wasn't primary API?All most apps need from
history
is topush
orreplace
. What if we just don't exposehistory
at all, and just expose some functions that wrap history to do what you need inside of components, like maybereplaceLocation
andpushLocation
.What would it look like?
What about redux and friends?
For people who want their own history (for redux actions, etc.) we just use whatever history they give us without wrapping it, in most cases would be as easy as
import { browserHistory } from 'react-router'
.Sales pitch
history
in router, we can moveuseRoutes
out into its own package and document it there.useRoutes
is useful for non-react apps, makes sense to move it outI might work up a PR to hit with a stick.
The text was updated successfully, but these errors were encountered: