-
-
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
Fix initial routing state after match #2965
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -50,6 +50,7 @@ describe('server rendering', function () { | |
return ( | ||
<div className="Async"> | ||
<h1>Async</h1> | ||
<Link to="/async" activeClassName="async-is-active">Link</Link> | ||
</div> | ||
) | ||
} | ||
|
@@ -75,7 +76,7 @@ describe('server rendering', function () { | |
const AsyncRoute = { | ||
path: '/async', | ||
getComponent(location, cb) { | ||
setTimeout(cb(null, Async)) | ||
setTimeout(() => cb(null, Async)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How did this even work before? :| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It wasn't async before. |
||
} | ||
} | ||
|
||
|
@@ -154,31 +155,38 @@ describe('server rendering', function () { | |
describe('server/client consistency', function () { | ||
// Just render to static markup here to avoid having to normalize markup. | ||
|
||
it('should match for synchronous route', function (done) { | ||
it('should match for synchronous route', function () { | ||
let serverString | ||
|
||
match({ routes, location: '/dashboard' }, function (error, redirectLocation, renderProps) { | ||
const serverString = renderToStaticMarkup( | ||
serverString = renderToStaticMarkup( | ||
<RouterContext {...renderProps} /> | ||
) | ||
const browserString = renderToStaticMarkup( | ||
<Router {...renderProps} history={createMemoryHistory('/dashboard')} /> | ||
) | ||
|
||
expect(browserString).toEqual(serverString) | ||
done() | ||
}) | ||
|
||
const browserString = renderToStaticMarkup( | ||
<Router history={createMemoryHistory('/dashboard')} routes={routes} /> | ||
) | ||
|
||
expect(browserString).toEqual(serverString) | ||
}) | ||
|
||
it('should match for asynchronous route', function (done) { | ||
match({ routes, location: '/async' }, function (error, redirectLocation, renderProps) { | ||
const serverString = renderToStaticMarkup( | ||
<RouterContext {...renderProps} /> | ||
) | ||
const browserString = renderToStaticMarkup( | ||
<Router {...renderProps} history={createMemoryHistory('/async')} /> | ||
) | ||
|
||
expect(browserString).toEqual(serverString) | ||
done() | ||
match({ history: createMemoryHistory('/async'), routes }, function (error, redirectLocation, renderProps) { | ||
const browserString = renderToStaticMarkup( | ||
<Router {...renderProps} /> | ||
) | ||
|
||
expect(browserString).toEqual(serverString) | ||
expect(browserString).toMatch(/async-is-active/) | ||
|
||
done() | ||
}) | ||
}) | ||
}) | ||
}) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,13 +11,13 @@ import { createRouterObject, createRoutingHistory } from './RouterUtils' | |
* This function matches a location to a set of routes and calls | ||
* callback(error, redirectLocation, renderProps) when finished. | ||
* | ||
* Note: You probably don't want to use this in a browser. Use | ||
* the history.listen API instead. | ||
* Note: You probably don't want to use this in a browser unless you're using | ||
* server-side rendering with async routes. | ||
*/ | ||
function match({ history, routes, location, ...options }, callback) { | ||
invariant( | ||
location, | ||
'match needs a location' | ||
history || location, | ||
'match needs a history or a location' | ||
) | ||
|
||
history = history ? history : createMemoryHistory(options) | ||
|
@@ -26,9 +26,19 @@ function match({ history, routes, location, ...options }, callback) { | |
createRoutes(routes) | ||
) | ||
|
||
// Allow match({ location: '/the/path', ... }) | ||
if (typeof location === 'string') | ||
location = history.createLocation(location) | ||
let unlisten | ||
|
||
if (location) { | ||
// Allow match({ location: '/the/path', ... }) | ||
if (typeof location === 'string') | ||
location = history.createLocation(location) | ||
} else { | ||
// Pick up the location from the history via synchronous history.listen | ||
// call if needed. | ||
unlisten = history.listen(historyLocation => { | ||
location = historyLocation | ||
}) | ||
} | ||
|
||
const router = createRouterObject(history, transitionManager) | ||
history = createRoutingHistory(history, transitionManager) | ||
|
@@ -37,8 +47,20 @@ function match({ history, routes, location, ...options }, callback) { | |
callback( | ||
error, | ||
redirectLocation, | ||
nextState && { ...nextState, history, router } | ||
nextState && { | ||
...nextState, | ||
history, | ||
router, | ||
matchContext: { history, transitionManager, router } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I get you're concerned that someone might have a mismatched So, this seems really weird to pass in the same things twice. It should just be the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, |
||
} | ||
) | ||
|
||
// Defer removing the listener to here to prevent DOM histories from having | ||
// to unwind DOM event listeners unnecessarily, in case callback renders a | ||
// <Router> and attaches another history listener. | ||
if (unlisten) { | ||
unlisten() | ||
} | ||
}) | ||
} | ||
|
||
|
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.
How do you get location from the server request into match?
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.
browserHistory
should already have it in the URL.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.
No, this is server rendering. How do I get
req.url
in there?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.
This is actually the "what to do on the client when using server-side-rendering plus async routes" section.
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.
On the server, as above, you still
match({ routes, location })
and render a<RouterContext>
.On the client, here, you
match({ history, routes })
and render a<Router>
. Sincehistory
is going to be some browser history, it doesn't need to be told its location.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.
Ah, well that's just because we have shitty docs. What are "async routes"? I know what that means, but it's not explained anywhere else in the docs. This is an example of why they don't work and why we get so many of the same set of questions all the damn time.
Anyways, I do like that this neatens up the API a tad, as we don't have to pass through our history to the Router instance by hand. Sorry for the confusion here.
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'm actually less bothered by this specific case – both async routes and SSR are relatively advanced, so the documentation around the intersection of the two doesn't need to target beginners.
It's not just "we don't have to pass through our history" in this case though – it's actually necessary that both
match
and<Router>
use the samehistory
, since the transition manager (that we want to re-use) is already bound to a history.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.
Circling back up on this – thoughts? In 3.0, the requirement ought to be either a
history
or amatchContext
.