Skip to content

Commit b86509a

Browse files
committed
[added] useRoutes history enhancer
[added] RoutingContext component [added] RouteContext mixin [added] Lifecycle mixin
1 parent cf0419f commit b86509a

20 files changed

+624
-480
lines changed

modules/AsyncUtils.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ export function loopAsync(turns, work, callback) {
1212
return;
1313

1414
if (currentTurn < turns) {
15-
currentTurn += 1;
16-
work.call(this, currentTurn - 1, next, done);
15+
work.call(this, currentTurn++, next, done);
1716
} else {
1817
done.apply(this, arguments);
1918
}

modules/Lifecycle.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import React from 'react';
2+
import invariant from 'invariant';
3+
4+
var { object } = React.PropTypes;
5+
6+
/**
7+
* The Lifecycle mixin adds the routerWillLeave lifecycle method
8+
* to a component that may be used to cancel a transition or prompt
9+
* the user for confirmation.
10+
*
11+
* On standard transitions, routerWillLeave receives a single argument: the
12+
* location we're transitioning to. To cancel the transition, return false.
13+
* To prompt the user for confirmation, return a prompt message (string).
14+
*
15+
* routerWillLeave does not receive a location object during the beforeunload
16+
* event in web browsers (assuming you're using the useBeforeUnload history
17+
* enhancer). In this case, it is not possible for us to know the location
18+
* we're transitioning to so routerWillLeave must return a prompt message to
19+
* prevent the user from closing the tab.
20+
*/
21+
var Lifecycle = {
22+
23+
propTypes: {
24+
// Route components receive the route object as a prop.
25+
route: object
26+
},
27+
28+
contextTypes: {
29+
router: object.isRequired,
30+
// Nested children receive the route as context, either
31+
// set by the route component using the RouteContext mixin
32+
// or by some other ancestor.
33+
route: object
34+
},
35+
36+
_getRoute() {
37+
var route = this.props.route || this.context.route;
38+
39+
invariant(
40+
route,
41+
'The Lifecycle mixin needs to be used either on 1) a <Route component> or ' +
42+
'2) a descendant of a <Route component> that uses the RouteContext mixin'
43+
);
44+
45+
return route;
46+
},
47+
48+
componentWillMount() {
49+
invariant(
50+
this.routerWillLeave,
51+
'The Lifecycle mixin requires you to define a routerWillLeave method'
52+
);
53+
54+
this.context.router.registerRouteHook(
55+
this._getRoute(),
56+
this.routerWillLeave
57+
);
58+
},
59+
60+
componentWillUnmount() {
61+
this.context.router.unregisterRouteHook(
62+
this._getRoute(),
63+
this.routerWillLeave
64+
);
65+
}
66+
67+
};
68+
69+
export default Lifecycle;

modules/Link.js

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { createClass, createElement, PropTypes } from 'react';
1+
import React from 'react';
2+
import warning from 'warning';
23

3-
var { object, string, func } = PropTypes;
4+
var { object, string, func } = React.PropTypes;
45

56
function isLeftClickEvent(event) {
67
return event.button === 0;
@@ -11,24 +12,24 @@ function isModifiedEvent(event) {
1112
}
1213

1314
/**
14-
* <Link> components are used to create an <a> element that links to a route.
15-
* When that route is active, the link gets an "active" class name (or the
16-
* value of its `activeClassName` prop).
15+
* A <Link> is used to create an <a> element that links to a route.
16+
* When that route is active, the link gets an "active" class name
17+
* (or the value of its `activeClassName` prop).
1718
*
1819
* For example, assuming you have the following route:
1920
*
20-
* <Route name="showPost" path="/posts/:postID" handler={Post}/>
21+
* <Route path="/posts/:postID" component={Post} />
2122
*
2223
* You could use the following component to link to that route:
2324
*
2425
* <Link to={`/posts/${post.id}`} />
2526
*
26-
* Links may pass along query string parameters
27-
* using the `query` prop.
27+
* Links may pass along location state and/or query string parameters
28+
* in the state/query props, respectively.
2829
*
29-
* <Link to="/posts/123" query={{ show: true }}/>
30+
* <Link ... query={{ show: true }} state={{ the: 'state' }} />
3031
*/
31-
var Link = createClass({
32+
var Link = React.createClass({
3233

3334
contextTypes: {
3435
router: object
@@ -67,33 +68,43 @@ var Link = createClass({
6768
event.preventDefault();
6869

6970
if (allowTransition)
70-
this.context.router.transitionTo(this.props.to, this.props.query, this.props.state);
71+
this.context.router.pushState(this.props.state, this.props.to, this.props.query);
72+
},
73+
74+
componentWillMount() {
75+
warning(
76+
this.context.router,
77+
'A <Link> should not be rendered outside the context of a router; ' +
78+
'some features including real hrefs, active styling, and navigation ' +
79+
'will not function correctly'
80+
);
7181
},
7282

7383
render() {
74-
var { router } = this.context;
84+
var { to, query } = this.props;
7585

76-
var props = Object.assign({}, this.props, {
86+
var props = {
87+
...this.props,
7788
onClick: this.handleClick
78-
});
89+
};
7990

80-
// Ignore if rendered outside of the context of a
81-
// router, simplifies unit testing.
82-
if (router) {
83-
var { to, query } = this.props;
91+
var { router } = this.context;
8492

93+
// Ignore if rendered outside the context
94+
// of a router, simplifies unit testing.
95+
if (router) {
8596
props.href = router.createHref(to, query);
8697

8798
if (router.isActive(to, query)) {
8899
if (props.activeClassName)
89100
props.className += props.className !== '' ? ` ${props.activeClassName}` : props.activeClassName;
90101

91102
if (props.activeStyle)
92-
props.style = Object.assign({}, props.style, props.activeStyle);
103+
props.style = { ...props.style, ...props.activeStyle };
93104
}
94105
}
95106

96-
return createElement('a', props);
107+
return React.createElement('a', props);
97108
}
98109

99110
});

modules/Navigation.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import React from 'react';
33
var { object } = React.PropTypes;
44

55
/**
6-
* A mixin for components that modify the URL.
6+
* The Navigation mixin provides methods for components
7+
* that need to modify the URL.
78
*
89
* Example:
910
*
@@ -26,15 +27,21 @@ var Navigation = {
2627

2728
contextTypes: {
2829
router: object.isRequired
30+
},
31+
32+
transitionTo(pathname, query, state) {
33+
return this.context.router.pushState(state, pathname, query);
34+
},
35+
36+
replaceWith(pathname, query, state) {
37+
return this.context.router.replaceState(state, pathname, query);
2938
}
3039

3140
};
3241

3342
var RouterNavigationMethods = [
3443
'createPath',
3544
'createHref',
36-
'transitionTo',
37-
'replaceWith',
3845
'go',
3946
'goBack',
4047
'goForward'

modules/NavigationMixin.js

Lines changed: 0 additions & 103 deletions
This file was deleted.

modules/NavigationTypes.js

Lines changed: 0 additions & 27 deletions
This file was deleted.

modules/Redirect.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1+
import React from 'react';
12
import invariant from 'invariant';
2-
import { createClass, PropTypes } from 'react';
33
import { createRouteFromReactElement } from './RouteUtils';
44
import { formatPattern } from './PatternUtils';
55
import { falsy } from './PropTypes';
66

7-
var { string, object } = PropTypes;
7+
var { string, object } = React.PropTypes;
88

99
/**
1010
* A <Redirect> is used to declare another URL path a client should be sent
@@ -13,7 +13,7 @@ var { string, object } = PropTypes;
1313
* Redirects are placed alongside routes in the route configuration and are
1414
* traversed in the same manner.
1515
*/
16-
var Redirect = createClass({
16+
var Redirect = React.createClass({
1717

1818
statics: {
1919

modules/Route.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1+
import React from 'react';
12
import warning from 'warning';
23
import invariant from 'invariant';
3-
import { createClass, PropTypes } from 'react';
44
import { createRouteFromReactElement } from './RouteUtils';
55
import { component, components } from './PropTypes';
66

7-
var { string, bool, func } = PropTypes;
7+
var { string, bool, func } = React.PropTypes;
88

99
/**
1010
* A <Route> is used to declare which components are rendered to the page when
@@ -16,15 +16,19 @@ var { string, bool, func } = PropTypes;
1616
* "active" and their components are rendered into the DOM, nested in the same
1717
* order as they are in the tree.
1818
*/
19-
var Route = createClass({
19+
var Route = React.createClass({
2020

2121
statics: {
2222

2323
createRouteFromReactElement(element) {
2424
var route = createRouteFromReactElement(element);
2525

2626
if (route.handler) {
27-
warning(false, '<Route handler> is deprecated, use <Route component> instead');
27+
warning(
28+
false,
29+
'<Route handler> is deprecated, use <Route component> instead'
30+
);
31+
2832
route.component = route.handler;
2933
delete route.handler;
3034
}

0 commit comments

Comments
 (0)