Skip to content

Commit 3770fb8

Browse files
committed
Second attempt, still ongoing
1 parent e0d4e1a commit 3770fb8

14 files changed

+533
-4
lines changed

modules/__tests__/Router-test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import expect from 'expect';
22
import React from 'react';
33
import { createLocation } from 'history';
4-
import Router from '../Router';
4+
import Router from '../take-2/Router';
55
import Route from '../Route';
66

77
describe('Router', function () {

modules/addNavigation.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
* @returns {CreateHistory}
66
*/
77
export default function addNavigation(createHistory) {
8-
return (...args) => {
9-
const history = createHistory(...args);
8+
return options => {
9+
const history = createHistory(options);
1010

1111
/**
1212
* Pushes a new Location onto the history stack.

modules/take-2/Router.js

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
import invariant from 'invariant';
2+
import React, { Component, PropTypes } from 'react';
3+
import * as RouterPropTypes from '../PropTypes';
4+
import BaseRouterRenderer from '../RouterRenderer';
5+
import createReactRouter from './createReactRouter';
6+
import routerContext from './routerContext';
7+
import compose from '../compose';
8+
import { parseQueryString } from '../QueryUtils';
9+
import createMemoryHistory from 'history/lib/createMemoryHistory';
10+
import useQueries from 'history/lib/useQueries';
11+
import disableListen from './disableListen';
12+
import matchUntilResolved from './matchUntilResolved';
13+
14+
function noop() {}
15+
16+
/**
17+
* For backwards compatibiliy, when no history is provided, use this history-
18+
* creating function
19+
*/
20+
const createFallbackHistory = compose(
21+
disableListen,
22+
useQueries,
23+
createMemoryHistory
24+
);
25+
26+
export default class Router extends Component {
27+
static propTypes = {
28+
createElement: PropTypes.func,
29+
parseQueryString: PropTypes.func,
30+
onError: PropTypes.func,
31+
onUpdate: PropTypes.func,
32+
routes: RouterPropTypes.routes,
33+
// Routes may also be given as children (JSX)
34+
children: RouterPropTypes.routes,
35+
36+
// Unit testing, simple server
37+
location: RouterPropTypes.location,
38+
39+
// Flux, data fetching
40+
// @deprecated, use <RouterRenderer {...initialState} /> instead
41+
initialState: PropTypes.object
42+
}
43+
44+
static defaultProps = {
45+
parseQueryString,
46+
onError: error => { throw error; },
47+
onUpdate: noop
48+
}
49+
50+
/**
51+
* Server-side shortcut for `router.match()`. Use `router.match()`
52+
* directly instead.
53+
* @deprecated
54+
* @param {Array<Route>} routes
55+
* @param {Location} location
56+
* @param {Function} callback
57+
* @param {Object} [initialState]
58+
*/
59+
static run(routes, location, callback, initialState) {
60+
const router = createReactRouter(createFallbackHistory)({
61+
routes,
62+
initialState
63+
});
64+
router.match(initialState, location, callback);
65+
}
66+
67+
constructor(props, context) {
68+
super(props, context);
69+
const {
70+
history,
71+
routes,
72+
children,
73+
location,
74+
parseQueryString,
75+
onError,
76+
initialState // Initial router state, not initial <Router> component state
77+
} = props;
78+
79+
this.state = {
80+
isTransitioning: false,
81+
location
82+
};
83+
84+
if (history) {
85+
this.router = createReactRouter(() => history)({
86+
routes: routes || children,
87+
initialState
88+
});
89+
this.unlisten = this.router.listen(this.handleRouterChange);
90+
} else if (location) {
91+
this.router = createReactRouter(createFallbackHistory)({
92+
routes: routes || children,
93+
initialState
94+
});
95+
matchUntilResolved(this.router.match)(
96+
this.router.getState(),
97+
location,
98+
(error, newState) => {
99+
if (error) {
100+
onError(error);
101+
}
102+
this.setState(newState);
103+
}
104+
);
105+
}
106+
107+
this.RouterRenderer = routerContext(this.router)(BaseRouterRenderer);
108+
}
109+
110+
setState(state, onUpdate) {
111+
if (!this.componentHasMounted) {
112+
this.state = { ...this.state, ...state };
113+
return;
114+
}
115+
super.setState(state, onUpdate);
116+
}
117+
118+
componentDidMount() {
119+
this.componentHasMounted = true;
120+
}
121+
122+
componentWillUnmount() {
123+
if (this.unlisten) {
124+
this.unlisten();
125+
}
126+
}
127+
128+
handleRouterChange = state => {
129+
this.setState({
130+
isTransitioning: state.pendingLocation ? true : false,
131+
...state
132+
});
133+
}
134+
135+
136+
// Below are deprecated methods that are added here for 1.0
137+
// compatibility. Future versions should access these on either the router
138+
// directly. Outside modules (like <Link>) should not use these methods — they
139+
// should use the correct methods, and rely on their own fallbacks
140+
// for compatibility.
141+
142+
transitionTo(...args) {
143+
const { router } = this;
144+
145+
invariant(
146+
history,
147+
'Router#transitionTo needs history'
148+
);
149+
150+
return router.transitionTo(...args);
151+
}
152+
153+
154+
replaceWith(...args) {
155+
const { router } = this;
156+
157+
invariant(
158+
history,
159+
'Router#replaceWith needs history'
160+
);
161+
162+
return router.replaceWith(...args);
163+
}
164+
165+
go(n) {
166+
const { router } = this;
167+
168+
invariant(
169+
router,
170+
'Router#go needs history'
171+
);
172+
173+
return router.go(n);
174+
}
175+
176+
goBack() {
177+
return this.go(-1);
178+
}
179+
180+
goForward() {
181+
return this.go(1);
182+
}
183+
184+
isActive(...args) {
185+
return this.router.isActive(...args);
186+
}
187+
188+
createHref(...args) {
189+
return this.router.createHref(...args);
190+
}
191+
192+
render() {
193+
const { router, history, RouterRenderer } = this;
194+
const { createElement, initialState } = this.props;
195+
196+
const state = initialState || this.state;
197+
198+
return (
199+
<RouterRenderer
200+
createElement={createElement}
201+
{...state}
202+
/>
203+
);
204+
}
205+
}

modules/take-2/addRouting.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
function identity(t) {
2+
return t;
3+
}
4+
5+
/**
6+
* History enhancer that adds routing capabilities. Takes middleware and
7+
* an initial state.
8+
* const createRouter = addRouting(middleware, initialState)(createHistory)
9+
* @param {Middleware} middleware - defaults to identity function
10+
* @param {Object} initialState
11+
*/
12+
export default function addRouting(middleware = identity, initialState) {
13+
return createHistory => historyOptions => {
14+
const history = createHistory(historyOptions);
15+
let state = initialState;
16+
let listeners = [];
17+
18+
const match = middleware((prevState, location, callback) => {
19+
callback(null, prevState);
20+
});
21+
22+
function listen(listener) {
23+
listeners.push(listener);
24+
listener(state);
25+
return function unlisten() {
26+
listeners = listeners.filter(l => l !== listener);
27+
};
28+
}
29+
30+
function getState() {
31+
return state;
32+
}
33+
34+
/**
35+
* @private
36+
*/
37+
function replaceState(nextState) {
38+
state = nextState;
39+
listeners.forEach(l => l(state));
40+
}
41+
42+
history.listen(location => {
43+
replaceState({ ...getState(), pendingLocation: location });
44+
45+
match(prevState, location, (err, nextState, redirectInfo) => {
46+
if (error) {
47+
throw error;
48+
}
49+
50+
if (redirectInfo) {
51+
history.pushState(
52+
redirectInfo.pathname,
53+
redirectInfo.query,
54+
redirectInfo.state
55+
);
56+
return;
57+
}
58+
59+
// Ensure this is the latest location
60+
if (location === getState().pendingLocation) {
61+
replaceState(nextState);
62+
}
63+
});
64+
});
65+
66+
return {
67+
...history,
68+
listen,
69+
getState,
70+
match
71+
};
72+
};
73+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import compose from '../compose';
2+
3+
export default function composeMiddleware(...middlewares) {
4+
return routes => {
5+
middlewares = middlewares.map(m => m(routes));
6+
return match => compose(
7+
...middlewares,
8+
match
9+
);
10+
};
11+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import createRouter from './createRouter';
2+
import addNavigation from '../addNavigation';
3+
import { createRoutes } from '../RouteUtils';
4+
import useTransitionHooks from './useTransitionHooks';
5+
import useRoutes from './useRoutes';
6+
import useComponents from './useComponents';
7+
import composeMiddleware from './composeMiddleware';
8+
import compose from '../compose';
9+
10+
function identityMiddleware() {
11+
return match => match
12+
}
13+
14+
export default function createReactRouter(createHistory) {
15+
return options => {
16+
const { middleware, routes } = options;
17+
18+
const nextOptions = {
19+
...options,
20+
middleware: composeMiddleware(
21+
middleware || identityMiddleware,
22+
useTransitionHooks,
23+
useRoutes,
24+
useComponents
25+
),
26+
routes: createRoutes(routes),
27+
};
28+
29+
return compose(
30+
addNavigation,
31+
createRouter,
32+
createHistory
33+
)(nextOptions);
34+
};
35+
}

0 commit comments

Comments
 (0)