Skip to content

Commit c1167fb

Browse files
authored
Merge pull request #3556 from avanderhoorn/update-getChildRoutes-with-progressState
Make `params` available to getChildRoutes providers
2 parents dbdd09b + 2aa9a9e commit c1167fb

File tree

9 files changed

+84
-35
lines changed

9 files changed

+84
-35
lines changed

docs/API.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ A function used to convert an object from [`<Link>`](#link)s or calls to
8585
A function used to convert a query string into an object that gets passed to route component props.
8686

8787
##### `onError(error)`
88-
While the router is matching, errors may bubble up, here is your opportunity to catch and deal with them. Typically these will come from async features like [`route.getComponents`](#getcomponentsnextstate-callback), [`route.getIndexRoute`](#getindexroutelocation-callback), and [`route.getChildRoutes`](#getchildrouteslocation-callback).
88+
While the router is matching, errors may bubble up, here is your opportunity to catch and deal with them. Typically these will come from async features like [`route.getComponents`](#getcomponentsnextstate-callback), [`route.getIndexRoute`](#getindexroutelocation-callback), and [`route.getChildRoutes`](#getchildroutespartialnextstate-callback).
8989

9090
##### `onUpdate()`
9191
Called whenever the router updates its state in response to URL changes.
@@ -368,8 +368,8 @@ A plain JavaScript object route definition. `<Router>` turns JSX `<Route>`s into
368368
##### `childRoutes`
369369
An array of child routes, same as `children` in JSX route configs.
370370

371-
##### `getChildRoutes(location, callback)`
372-
Same as `childRoutes` but asynchronous and receives the `location`. Useful for code-splitting and dynamic route matching (given some state or session data to return a different set of child routes).
371+
##### `getChildRoutes(partialNextState, callback)`
372+
Same as `childRoutes` but asynchronous and receives the `partialNextState`. Useful for code-splitting and dynamic route matching (given some state or session data to return a different set of child routes).
373373

374374
###### `callback` signature
375375
`cb(err, routesArray)`
@@ -399,8 +399,8 @@ let myRoute = {
399399

400400
let myRoute = {
401401
path: 'picture/:id',
402-
getChildRoutes(location, cb) {
403-
let { state } = location
402+
getChildRoutes(partialNextState, cb) {
403+
let { state } = partialNextState
404404

405405
if (state && state.fromDashboard) {
406406
cb(null, [dashboardPictureRoute])

docs/guides/DynamicRouting.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ A router is the perfect place to handle code splitting: it's responsible for set
1010

1111
React Router does all of its [path matching](/docs/guides/RouteMatching.md) and component fetching asynchronously, which allows you to not only load up the components lazily, *but also lazily load the route configuration*. You really only need one route definition in your initial bundle, the router can resolve the rest on demand.
1212

13-
Routes may define [`getChildRoutes`](/docs/API.md#getchildrouteslocation-callback), [`getIndexRoute`](/docs/API.md#getindexroutelocation-callback), and [`getComponents`](/docs/API.md#getcomponentsnextstate-callback) methods. These are asynchronous and only called when needed. We call it "gradual matching". React Router will gradually match the URL and fetch only the amount of route configuration and components it needs to match the URL and render.
13+
Routes may define [`getChildRoutes`](/docs/API.md#getchildroutespartialnextstate-callback), [`getIndexRoute`](/docs/API.md#getindexroutelocation-callback), and [`getComponents`](/docs/API.md#getcomponentsnextstate-callback) methods. These are asynchronous and only called when needed. We call it "gradual matching". React Router will gradually match the URL and fetch only the amount of route configuration and components it needs to match the URL and render.
1414

1515
Coupled with a smart code splitting tool like [webpack](http://webpack.github.io/), a once tiresome architecture is now simple and declarative.
1616

1717
```js
1818
const CourseRoute = {
1919
path: 'course/:courseId',
2020

21-
getChildRoutes(location, callback) {
21+
getChildRoutes(partialNextState, callback) {
2222
require.ensure([], function (require) {
2323
callback(null, [
2424
require('./routes/Announcements'),

examples/huge-apps/routes/Course/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module.exports = {
22
path: 'course/:courseId',
33

4-
getChildRoutes(location, cb) {
4+
getChildRoutes(partialNextState, cb) {
55
require.ensure([], (require) => {
66
cb(null, [
77
require('./routes/Announcements'),

examples/huge-apps/routes/Course/routes/Announcements/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module.exports = {
22
path: 'announcements',
33

4-
getChildRoutes(location, cb) {
4+
getChildRoutes(partialNextState, cb) {
55
require.ensure([], (require) => {
66
cb(null, [
77
require('./routes/Announcement')

examples/huge-apps/routes/Course/routes/Assignments/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module.exports = {
22
path: 'assignments',
33

4-
getChildRoutes(location, cb) {
4+
getChildRoutes(partialNextState, cb) {
55
require.ensure([], (require) => {
66
cb(null, [
77
require('./routes/Assignment')

modules/__tests__/matchRoutes-test.js

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ describe('matchRoutes', function () {
262262
if (childRoutes) {
263263
delete route.childRoutes
264264

265-
route.getChildRoutes = function (location, callback) {
265+
route.getChildRoutes = function (partialNextState, callback) {
266266
setTimeout(function () {
267267
callback(null, childRoutes)
268268
})
@@ -291,10 +291,10 @@ describe('matchRoutes', function () {
291291
})
292292

293293
describe('an asynchronous JSX route config', function () {
294-
let getChildRoutes, getIndexRoute, jsxRoutes
294+
let getChildRoutes, getIndexRoute, jsxRoutes, makeJsxNestedRoutes
295295

296296
beforeEach(function () {
297-
getChildRoutes = function (location, callback) {
297+
getChildRoutes = function (partialNextState, callback) {
298298
setTimeout(function () {
299299
callback(null, <Route path=":userID" />)
300300
})
@@ -312,6 +312,19 @@ describe('matchRoutes', function () {
312312
getChildRoutes={getChildRoutes}
313313
getIndexRoute={getIndexRoute} />
314314
])
315+
316+
makeJsxNestedRoutes = () => {
317+
const spy = expect.spyOn({ getChildRoutes }, 'getChildRoutes').andCallThrough()
318+
const routes = createRoutes([
319+
<Route name="users"
320+
path="users/:id">
321+
<Route name="topic"
322+
path=":topic"
323+
getChildRoutes={spy} />
324+
</Route>
325+
])
326+
return { spy, routes }
327+
}
315328
})
316329

317330
it('when getChildRoutes callback returns reactElements', function (done) {
@@ -330,6 +343,16 @@ describe('matchRoutes', function () {
330343
done()
331344
})
332345
})
346+
347+
it('when getChildRoutes callback returns partialNextState', function (done) {
348+
const jsxNestedRoutes = makeJsxNestedRoutes()
349+
matchRoutes(jsxNestedRoutes.routes, createLocation('/users/5/details/others'), function () {
350+
const state = jsxNestedRoutes.spy.calls[0].arguments[0]
351+
expect(state).toExist()
352+
expect(state.params).toEqual({ id: '5', topic: 'details' })
353+
done()
354+
})
355+
})
333356
})
334357

335358
it('complains about invalid index route with path', function (done) {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import warning from './routerWarning'
2+
import { canUseMembrane } from './deprecateObjectProperties'
3+
4+
// No-op by default.
5+
let deprecateLocationProperties = () => {}
6+
7+
if (__DEV__ && canUseMembrane) {
8+
deprecateLocationProperties = (nextState, location) => {
9+
const nextStateWithLocation = { ...nextState }
10+
11+
// I don't use deprecateObjectProperties here because I want to keep the
12+
// same code path between development and production, in that we just
13+
// assign extra properties to the copy of the state object in both cases.
14+
for (const prop in location) {
15+
if (!Object.prototype.hasOwnProperty.call(location, prop)) {
16+
continue
17+
}
18+
19+
Object.defineProperty(nextStateWithLocation, prop, {
20+
get() {
21+
warning(false, 'Accessing location properties from the first argument to `getComponent` and `getComponents` is deprecated. That argument is now the router state (`nextState`) rather than the location. To access the location, use `nextState.location`.')
22+
return location[prop]
23+
}
24+
})
25+
}
26+
27+
return nextStateWithLocation
28+
}
29+
}
30+
31+
export default deprecateLocationProperties

modules/getComponents.js

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { mapAsync } from './AsyncUtils'
22
import { canUseMembrane } from './deprecateObjectProperties'
3-
import warning from './routerWarning'
3+
import deprecateLocationProperties from './deprecateLocationProperties'
44

55
function getComponentsForRoute(nextState, route, callback) {
66
if (route.component || route.components) {
@@ -18,23 +18,7 @@ function getComponentsForRoute(nextState, route, callback) {
1818
let nextStateWithLocation
1919

2020
if (__DEV__ && canUseMembrane) {
21-
nextStateWithLocation = { ...nextState }
22-
23-
// I don't use deprecateObjectProperties here because I want to keep the
24-
// same code path between development and production, in that we just
25-
// assign extra properties to the copy of the state object in both cases.
26-
for (const prop in location) {
27-
if (!Object.prototype.hasOwnProperty.call(location, prop)) {
28-
continue
29-
}
30-
31-
Object.defineProperty(nextStateWithLocation, prop, {
32-
get() {
33-
warning(false, 'Accessing location properties from the first argument to `getComponent` and `getComponents` is deprecated. That argument is now the router state (`nextState`) rather than the location. To access the location, use `nextState.location`.')
34-
return location[prop]
35-
}
36-
})
37-
}
21+
nextStateWithLocation = deprecateLocationProperties(nextState, location)
3822
} else {
3923
nextStateWithLocation = { ...nextState, ...location }
4024
}

modules/matchRoutes.js

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,29 @@ import warning from './routerWarning'
22
import { loopAsync } from './AsyncUtils'
33
import { matchPattern } from './PatternUtils'
44
import { createRoutes } from './RouteUtils'
5+
import { canUseMembrane } from './deprecateObjectProperties'
6+
import deprecateLocationProperties from './deprecateLocationProperties'
57

6-
function getChildRoutes(route, location, callback) {
8+
function getChildRoutes(route, location, paramNames, paramValues, callback) {
79
if (route.childRoutes) {
810
return [ null, route.childRoutes ]
911
}
1012
if (!route.getChildRoutes) {
1113
return []
1214
}
1315

14-
let sync = true, result
16+
let sync = true, result, partialNextStateWithLocation
17+
const partialNextState = {
18+
params: createParams(paramNames, paramValues)
19+
}
20+
21+
if (__DEV__ && canUseMembrane) {
22+
partialNextStateWithLocation = deprecateLocationProperties(partialNextState, location)
23+
} else {
24+
partialNextStateWithLocation = { ...partialNextState, ...location }
25+
}
1526

16-
route.getChildRoutes(location, function (error, childRoutes) {
27+
route.getChildRoutes(partialNextStateWithLocation, function (error, childRoutes) {
1728
childRoutes = !error && createRoutes(childRoutes)
1829
if (sync) {
1930
result = [ error, childRoutes ]
@@ -160,7 +171,7 @@ function matchRouteDeep(
160171
}
161172
}
162173

163-
const result = getChildRoutes(route, location, onChildRoutes)
174+
const result = getChildRoutes(route, location, paramNames, paramValues, onChildRoutes)
164175
if (result) {
165176
onChildRoutes(...result)
166177
}

0 commit comments

Comments
 (0)