Socket
Socket
Sign inDemoInstall

react-router

Package Overview
Dependencies
Maintainers
2
Versions
506
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-router - npm Package Compare versions

Comparing version 0.8.0 to 0.9.0

docs/api/mixins/CurrentPath.md

30

CHANGELOG.md

@@ -0,22 +1,28 @@

v0.9.0 - Mon, 06 Oct 2014 19:37:27 GMT
--------------------------------------
- [5aae2a8](../../commit/5aae2a8) [added] onChange event to Routes
- [ba65269](../../commit/ba65269) [removed] AsyncState
v0.8.0 - Sat, 04 Oct 2014 05:39:02 GMT
--------------------------------------
- [4d8026f](../../commit/4d8026f) [fixed] Corrected CONTRIBUTING.md by replacing 'script' path with 'scripts'
- [d2aa7cb](../../commit/d2aa7cb) [added] <Routes location="none">
- [637c0ac](../../commit/637c0ac) [added] <Routes fixedPath>
- [d2aa7cb](../../commit/d2aa7cb) [added] `<Routes location="none">`
- [637c0ac](../../commit/637c0ac) [added] `<Routes fixedPath>`
- [f2bf4bd](../../commit/f2bf4bd) [removed] RouteStore
- [47f0599](../../commit/47f0599) [changed] Remove preserveScrollPosition, add scrollStrategy
- [2f014b7](../../commit/2f014b7) [fixed] Document the name prop passed to RouteHandlers
- [f2bf4bd](../../commit/f2bf4bd) [added] Router.PathState for keeping track of the current URL path
- [f2bf4bd](../../commit/f2bf4bd) [added] Router.RouteLookup for looking up routes
- [f2bf4bd](../../commit/f2bf4bd) [added] Router.Transitions for transitioning to other routes
- [f2bf4bd](../../commit/f2bf4bd) [added] Pluggable scroll behaviors
- [f2bf4bd](../../commit/f2bf4bd) [changed] `<Routes preserveScrollPosition>` => `<Routes scrollBehavior>`
- [f2bf4bd](../../commit/f2bf4bd) [removed] `<Route preserveScrollPosition>`
- [f2bf4bd](../../commit/f2bf4bd) [removed] Router.transitionTo, Router.replaceWith, Router.goBack
- [97dbf2d](../../commit/97dbf2d) [added] transition.wait(promise)
- [cc9f145](../../commit/cc9f145) [changed] Give path listeners a chance to update state before mounting
- [6af24bd](../../commit/6af24bd) [changed] Give ActiveState a chance to update state before mounting
- [3787179](../../commit/3787179) [changed] Transition retry now uses replaceWith.
- [1b16b56](../../commit/1b16b56) [fixed] syntax error in documentation overview
- [e0b708f](../../commit/e0b708f) [added] Ability to transitionTo absolute URLs
- [c1493b5](../../commit/c1493b5) [changed] #259 support dots in named params
- [4849166](../../commit/4849166) [changed] Renamed Routes#dispatch => Routes#transitionTo
- [c373d10](../../commit/c373d10) [changed] Only replaceWith/goBack when DOM is available
- [a4ce7c8](../../commit/a4ce7c8) [changed] isActive is an instance method [removed] <Routes onActiveStateChange>
- [a4ce7c8](../../commit/a4ce7c8) [changed] isActive is an instance method
- [a4ce7c8](../../commit/a4ce7c8) [removed] `<Routes onActiveStateChange>`
v0.7.0 - Tue, 02 Sep 2014 16:42:28 GMT

@@ -23,0 +29,0 @@ --------------------------------------

4

docs/api/components/Routes.md

@@ -42,6 +42,2 @@ API: `Routes` (component)

### `fixedPath`
TODO
### `onAbortedTransition`

@@ -48,0 +44,0 @@

@@ -10,7 +10,5 @@ API: `Location` (object)

### `setup(onChange)`
### `setup()`
Called when the router is first setup. Whenever an external actor should
cause the router to react, call `onChange` (for example, on
`window.hashchange`).
Called when the router is first setup.

@@ -34,6 +32,2 @@ ### `teardown`

### `getCurrentPath`
Should return the current path as a string.
### `toString`

@@ -52,3 +46,3 @@

setup: function (onChange) {},
setup: function () {},

@@ -63,4 +57,2 @@ teardown: function () {},

getCurrentPath: function () {},
toString: function () {}

@@ -70,2 +62,1 @@

```

@@ -10,2 +10,14 @@ API: `ActiveState` (mixin)

### `getActiveRoutes()`
Returns an array of the `<Route>`s that are currently active.
### `getActiveParams()`
Returns a hash of the currently active URL params.
### `getActiveQuery()`
Returns a hash of the currently active query params.
### `isActive(routeName, params, query)`

@@ -16,9 +28,2 @@

Lifecycle Methods
-----------------
### `updateActiveState`
Called when the active state changes.
Example

@@ -31,4 +36,4 @@ -------

```js
var Link = require('react-router/Link');
var ActiveState = require('react-router/ActiveState');
var Link = require('react-router').Link;
var ActiveState = require('react-router').ActiveState;

@@ -39,14 +44,5 @@ var Tab = React.createClass({

getInitialState: function () {
return { isActive: false };
},
updateActiveState: function () {
this.setState({
isActive: this.isActive(this.props.to, this.props.params, this.props.query)
})
},
render: function() {
var className = this.state.isActive ? 'active' : '';
var isActive = this.isActive(this.props.to, this.props.params, this.props.query);
var className = isActive ? 'active' : '';
var link = Link(this.props);

@@ -62,2 +58,1 @@ return <li className={className}>{link}</li>;

```

@@ -15,9 +15,10 @@ React Router API

- Misc
- [`transition`](/docs/api/misc/transition.md)
- Mixins
- [`ActiveState`](/docs/api/mixins/ActiveState.md)
- [`AsyncState`](/docs/api/mixins/AsyncState.md)
- [`CurrentPath`](/docs/api/mixins/CurrentPath.md)
- [`Navigation`](/docs/api/mixins/Navigation.md)
- Misc
- [`transition`](/docs/api/misc/transition.md)

@@ -27,8 +28,10 @@ Public Modules

All modules found at the repository root are considered public. You can
require them conveniently with `var Route = require('react-router/Route');` etc.
While there are many modules in this repository, only those found on the
default export are considered public.
Note that we do not support requiring modules from our `modules`
directory. (No notes in the changelog, no changes to the versioning of
the lib, etc.)
```js
var Router = require('react-router');
var Link = Router.Link // yes
var Link = require('react-router/modules/components/Link') // no
```

@@ -307,4 +307,4 @@ React Router Guide

their scroll position. If they visit a new route, it will automatically
scroll the window to the top. You can opt out of this with the
`preserverScrollPosition` option on [Routes][Routes] or [Route][Route].
scroll the window to the top. You can configure this options on
[Routes][Routes].

@@ -375,1 +375,13 @@ Bells and Whistles

CommonJS Guide
--------------
In order for the above examples to work in a CommonJS environment you'll need to `require` the following:
```
var Router = require('react-router');
var Route = Router.Route;
var Routes = Router.Routes;
var DefaultRoute = Router.DefaultRoute;
var Link = Router.Link;
```

@@ -5,14 +5,14 @@ var assert = require('assert');

var ReactTestUtils = React.addons.TestUtils;
var RouteContainer = require('../../mixins/RouteContainer');
var TransitionHandler = require('../../mixins/TransitionHandler');
var PathStore = require('../../stores/PathStore');
var DefaultRoute = require('../DefaultRoute');
var Routes = require('../Routes');
var Route = require('../Route');
var DefaultRoute = require('../DefaultRoute');
afterEach(function () {
// For some reason unmountComponentAtNode doesn't call componentWillUnmount :/
PathStore.removeAllChangeListeners();
var NullHandler = React.createClass({
render: function () {
return null;
}
});
describe('A DefaultRoute', function () {
it('has a null path', function () {

@@ -22,9 +22,2 @@ expect(DefaultRoute({ path: '/' }).props.path).toBe(null);

var App = React.createClass({
mixins: [ RouteContainer ],
render: function () {
return React.DOM.div();
}
});
describe('at the root of a container', function () {

@@ -34,4 +27,4 @@ var component, route;

component = ReactTestUtils.renderIntoDocument(
App(null,
route = DefaultRoute({ handler: App })
Routes({ location: 'none' },
route = DefaultRoute({ handler: NullHandler })
)

@@ -54,5 +47,5 @@ );

component = ReactTestUtils.renderIntoDocument(
App(null,
route = Route({ handler: App },
defaultRoute = DefaultRoute({ handler: App })
Routes({ location: 'none' },
route = Route({ handler: NullHandler },
defaultRoute = DefaultRoute({ handler: NullHandler })
)

@@ -71,11 +64,6 @@ )

});
});
describe('when no child routes match a URL, but the parent\'s path matches', function () {
var App = React.createClass({
mixins: [ TransitionHandler ],
render: function () {
return React.DOM.div();
}
});

@@ -85,8 +73,8 @@ var component, rootRoute, defaultRoute;

component = ReactTestUtils.renderIntoDocument(
App({ location: 'none' },
rootRoute = Route({ name: 'user', path: '/users/:id', handler: App },
Route({ name: 'home', path: '/users/:id/home', handler: App }),
Routes({ location: 'none' },
rootRoute = Route({ name: 'user', path: '/users/:id', handler: NullHandler },
Route({ name: 'home', path: '/users/:id/home', handler: NullHandler }),
// Make it the middle sibling to test order independence.
defaultRoute = DefaultRoute({ handler: App }),
Route({ name: 'news', path: '/users/:id/news', handler: App })
defaultRoute = DefaultRoute({ handler: NullHandler }),
Route({ name: 'news', path: '/users/:id/news', handler: NullHandler })
)

@@ -108,2 +96,3 @@ )

});
});

@@ -5,10 +5,37 @@ var assert = require('assert');

var ReactTestUtils = React.addons.TestUtils;
var PathStore = require('../../stores/PathStore');
var DefaultRoute = require('../DefaultRoute');
var Routes = require('../Routes');
var Route = require('../Route');
var Link = require('../Link');
describe('A Link', function () {
describe('with params and a query', function () {
var HomeHandler = React.createClass({
render: function () {
return Link({ ref: 'link', to: 'home', params: { username: 'mjackson' }, query: { awesome: true } });
}
});
var component;
beforeEach(function () {
component = ReactTestUtils.renderIntoDocument(
Routes({ location: 'history', initialPath: '/mjackson/home' },
Route({ name: 'home', path: '/:username/home', handler: HomeHandler })
)
);
});
afterEach(function () {
React.unmountComponentAtNode(component.getDOMNode());
});
it('knows how to make its href', function () {
var linkComponent = component.getActiveComponent().refs.link;
expect(linkComponent.getHref()).toEqual('/mjackson/home?awesome=true');
});
});
describe('when its route is active', function () {
var Home = React.createClass({
var HomeHandler = React.createClass({
render: function () {

@@ -23,3 +50,3 @@ return Link({ ref: 'link', to: 'home', className: 'a-link', activeClassName: 'highlight' });

Routes(null,
DefaultRoute({ name: 'home', handler: Home })
DefaultRoute({ name: 'home', handler: HomeHandler })
)

@@ -31,16 +58,10 @@ );

React.unmountComponentAtNode(component.getDOMNode());
// For some reason unmountComponentAtNode doesn't call componentWillUnmount :/
PathStore.removeAllChangeListeners();
});
it('is active', function () {
var linkComponent = component.getActiveRoute().refs.link;
assert(linkComponent.isActive);
});
it('has its active class name', function () {
var linkComponent = component.getActiveRoute().refs.link;
var linkComponent = component.getActiveComponent().refs.link;
expect(linkComponent.getClassName()).toEqual('a-link highlight');
});
});
});

@@ -5,14 +5,14 @@ var assert = require('assert');

var ReactTestUtils = React.addons.TestUtils;
var RouteContainer = require('../../mixins/RouteContainer');
var TransitionHandler = require('../../mixins/TransitionHandler');
var PathStore = require('../../stores/PathStore');
var NotFoundRoute = require('../NotFoundRoute');
var Routes = require('../Routes');
var Route = require('../Route');
var NotFoundRoute = require('../NotFoundRoute');
afterEach(function () {
// For some reason unmountComponentAtNode doesn't call componentWillUnmount :/
PathStore.removeAllChangeListeners();
var NullHandler = React.createClass({
render: function () {
return null;
}
});
describe('A NotFoundRoute', function () {
it('has a null path', function () {

@@ -22,9 +22,2 @@ expect(NotFoundRoute({ path: '/' }).props.path).toBe(null);

var App = React.createClass({
mixins: [ RouteContainer ],
render: function () {
return React.DOM.div();
}
});
describe('at the root of a container', function () {

@@ -34,4 +27,4 @@ var component, route;

component = ReactTestUtils.renderIntoDocument(
App(null,
route = NotFoundRoute({ handler: App })
Routes({ location: 'none' },
route = NotFoundRoute({ handler: NullHandler })
)

@@ -54,5 +47,5 @@ );

component = ReactTestUtils.renderIntoDocument(
App(null,
route = Route({ handler: App },
notFoundRoute = NotFoundRoute({ handler: App })
Routes({ location: 'none' },
route = Route({ handler: NullHandler },
notFoundRoute = NotFoundRoute({ handler: NullHandler })
)

@@ -71,11 +64,6 @@ )

});
});
describe('when no child routes match a URL, but the beginning of the parent\'s path matches', function () {
var App = React.createClass({
mixins: [ TransitionHandler ],
render: function () {
return React.DOM.div();
}
});

@@ -85,8 +73,8 @@ var component, rootRoute, notFoundRoute;

component = ReactTestUtils.renderIntoDocument(
App({ location: 'none' },
rootRoute = Route({ name: 'user', path: '/users/:id', handler: App },
Route({ name: 'home', path: '/users/:id/home', handler: App }),
Routes({ location: 'none' },
rootRoute = Route({ name: 'user', path: '/users/:id', handler: NullHandler },
Route({ name: 'home', path: '/users/:id/home', handler: NullHandler }),
// Make it the middle sibling to test order independence.
notFoundRoute = NotFoundRoute({ handler: App }),
Route({ name: 'news', path: '/users/:id/news', handler: App })
notFoundRoute = NotFoundRoute({ handler: NullHandler }),
Route({ name: 'news', path: '/users/:id/news', handler: NullHandler })
)

@@ -108,2 +96,3 @@ )

});
});

@@ -5,3 +5,2 @@ var assert = require('assert');

var ReactTestUtils = React.addons.TestUtils;
var PathStore = require('../../stores/PathStore');
var Routes = require('../Routes');

@@ -14,5 +13,6 @@ var Route = require('../Route');

afterEach(function () {
// For some reason unmountComponentAtNode doesn't call componentWillUnmount :/
PathStore.removeAllChangeListeners();
var NullHandler = React.createClass({
render: function () {
return null;
}
});

@@ -22,8 +22,2 @@

var App = React.createClass({
render: function () {
return null;
}
});
describe('that matches a URL', function () {

@@ -34,4 +28,4 @@ var component;

Routes(null,
Route({ handler: App },
Route({ path: '/a/b/c', handler: App })
Route({ handler: NullHandler },
Route({ path: '/a/b/c', handler: NullHandler })
)

@@ -61,4 +55,4 @@ )

Routes(null,
Route({ handler: App },
Route({ path: '/posts/:id/edit', handler: App })
Route({ handler: NullHandler },
Route({ path: '/posts/:id/edit', handler: NullHandler })
)

@@ -83,56 +77,2 @@ )

describe('when a transition is aborted', function () {
it('triggers onAbortedTransition', function (done) {
var App = React.createClass({
statics: {
willTransitionTo: function (transition) {
transition.abort();
}
},
render: function () {
return React.DOM.div();
}
});
function handleAbortedTransition(transition) {
assert(transition);
done();
}
ReactTestUtils.renderIntoDocument(
Routes({ onAbortedTransition: handleAbortedTransition },
Route({ handler: App })
)
);
});
});
describe('when there is an error in a transition hook', function () {
it('triggers onTransitionError', function (done) {
var App = React.createClass({
statics: {
willTransitionTo: function (transition) {
throw new Error('boom!');
}
},
render: function () {
return React.DOM.div();
}
});
function handleTransitionError(error) {
assert(error);
expect(error.message).toEqual('boom!');
done();
}
ReactTestUtils.renderIntoDocument(
Routes({ onTransitionError: handleTransitionError },
Route({ handler: App })
)
);
});
});
});
var React = require('react');
var merge = require('react/lib/merge');
var ActiveState = require('../mixins/ActiveState');
var Transitions = require('../mixins/Transitions');
var Navigation = require('../mixins/Navigation');

@@ -36,7 +36,7 @@ function isLeftClickEvent(event) {

mixins: [ ActiveState, Transitions ],
mixins: [ ActiveState, Navigation ],
propTypes: {
activeClassName: React.PropTypes.string.isRequired,
to: React.PropTypes.string.isRequired,
activeClassName: React.PropTypes.string.isRequired,
params: React.PropTypes.object,

@@ -53,26 +53,8 @@ query: React.PropTypes.object,

getInitialState: function () {
return {
isActive: false
};
},
updateActiveState: function () {
this.setState({
isActive: this.isActive(this.props.to, this.props.params, this.props.query)
});
},
componentWillReceiveProps: function (nextProps) {
this.setState({
isActive: this.isActive(nextProps.to, nextProps.params, nextProps.query)
});
},
handleClick: function (event) {
var allowTransition = true;
var onClickResult;
var clickResult;
if (this.props.onClick)
onClickResult = this.props.onClick(event);
clickResult = this.props.onClick(event);

@@ -82,3 +64,3 @@ if (isModifiedEvent(event) || !isLeftClickEvent(event))

if (onClickResult === false || event.defaultPrevented === true)
if (clickResult === false || event.defaultPrevented === true)
allowTransition = false;

@@ -106,3 +88,3 @@

if (this.state.isActive)
if (this.isActive(this.props.to, this.props.params, this.props.query))
className += ' ' + this.props.activeClassName;

@@ -109,0 +91,0 @@

var React = require('react');
var TransitionHandler = require('../mixins/TransitionHandler');
var warning = require('react/lib/warning');
var invariant = require('react/lib/invariant');
var canUseDOM = require('react/lib/ExecutionEnvironment').canUseDOM;
var copyProperties = require('react/lib/copyProperties');
var PathStore = require('../stores/PathStore');
var HashLocation = require('../locations/HashLocation');
var reversedArray = require('../utils/reversedArray');
var Transition = require('../utils/Transition');
var Redirect = require('../utils/Redirect');
var Path = require('../utils/Path');
var Route = require('./Route');
function makeMatch(route, params) {
return { route: route, params: params };
}
function getRootMatch(matches) {
return matches[matches.length - 1];
}
function findMatches(path, routes, defaultRoute, notFoundRoute) {
var matches = null, route, params;
for (var i = 0, len = routes.length; i < len; ++i) {
route = routes[i];
// Check the subtree first to find the most deeply-nested match.
matches = findMatches(path, route.props.children, route.props.defaultRoute, route.props.notFoundRoute);
if (matches != null) {
var rootParams = getRootMatch(matches).params;
params = route.props.paramNames.reduce(function (params, paramName) {
params[paramName] = rootParams[paramName];
return params;
}, {});
matches.unshift(makeMatch(route, params));
return matches;
}
// No routes in the subtree matched, so check this route.
params = Path.extractParams(route.props.path, path);
if (params)
return [ makeMatch(route, params) ];
}
// No routes matched, so try the default route if there is one.
if (defaultRoute && (params = Path.extractParams(defaultRoute.props.path, path)))
return [ makeMatch(defaultRoute, params) ];
// Last attempt: does the "not found" route match?
if (notFoundRoute && (params = Path.extractParams(notFoundRoute.props.path, path)))
return [ makeMatch(notFoundRoute, params) ];
return matches;
}
function hasMatch(matches, match) {
return matches.some(function (m) {
if (m.route !== match.route)
return false;
for (var property in m.params)
if (m.params[property] !== match.params[property])
return false;
return true;
});
}
function updateMatchComponents(matches, refs) {
var i = 0, component;
while (component = refs.__activeRoute__) {
matches[i++].component = component;
refs = component.refs;
}
}
/**
* Computes the next state for the given component and calls
* callback(error, nextState) when finished. Also runs all
* transition hooks along the way.
*/
function computeNextState(component, transition, callback) {
if (component.state.path === transition.path)
return callback(); // Nothing to do!
var currentMatches = component.state.matches;
var nextMatches = component.match(transition.path);
warning(
nextMatches,
'No route matches path "' + transition.path + '". Make sure you have ' +
'<Route path="' + transition.path + '"> somewhere in your routes'
);
if (!nextMatches)
nextMatches = [];
var fromMatches, toMatches;
if (currentMatches.length) {
updateMatchComponents(currentMatches, component.refs);
fromMatches = currentMatches.filter(function (match) {
return !hasMatch(nextMatches, match);
});
toMatches = nextMatches.filter(function (match) {
return !hasMatch(currentMatches, match);
});
} else {
fromMatches = [];
toMatches = nextMatches;
}
var query = Path.extractQuery(transition.path) || {};
runTransitionFromHooks(fromMatches, transition, function (error) {
if (error || transition.isAborted)
return callback(error);
runTransitionToHooks(toMatches, transition, query, function (error) {
if (error || transition.isAborted)
return callback(error);
var matches = currentMatches.slice(0, currentMatches.length - fromMatches.length).concat(toMatches);
var rootMatch = getRootMatch(matches);
var params = (rootMatch && rootMatch.params) || {};
var routes = matches.map(function (match) {
return match.route;
});
callback(null, {
path: transition.path,
matches: matches,
activeRoutes: routes,
activeParams: params,
activeQuery: query
});
});
});
}
/**
* Calls the willTransitionFrom hook of all handlers in the given matches
* serially in reverse with the transition object and the current instance of
* the route's handler, so that the deepest nested handlers are called first.
* Calls callback(error) when finished.
*/
function runTransitionFromHooks(matches, transition, callback) {
var hooks = reversedArray(matches).map(function (match) {
return function () {
var handler = match.route.props.handler;
if (!transition.isAborted && handler.willTransitionFrom)
return handler.willTransitionFrom(transition, match.component);
var promise = transition.promise;
delete transition.promise;
return promise;
};
});
runHooks(hooks, callback);
}
/**
* Calls the willTransitionTo hook of all handlers in the given matches
* serially with the transition object and any params that apply to that
* handler. Calls callback(error) when finished.
*/
function runTransitionToHooks(matches, transition, query, callback) {
var hooks = matches.map(function (match) {
return function () {
var handler = match.route.props.handler;
if (!transition.isAborted && handler.willTransitionTo)
handler.willTransitionTo(transition, match.params, query);
var promise = transition.promise;
delete transition.promise;
return promise;
};
});
runHooks(hooks, callback);
}
/**
* Runs all hook functions serially and calls callback(error) when finished.
* A hook may return a promise if it needs to execute asynchronously.
*/
function runHooks(hooks, callback) {
try {
var promise = hooks.reduce(function (promise, hook) {
// The first hook to use transition.wait makes the rest
// of the transition async from that point forward.
return promise ? promise.then(hook) : hook();
}, null);
} catch (error) {
return callback(error); // Sync error.
}
if (promise) {
// Use setTimeout to break the promise chain.
promise.then(function () {
setTimeout(callback);
}, function (error) {
setTimeout(function () {
callback(error);
});
});
} else {
callback();
}
}
function returnNull() {
return null;
}
function computeHandlerProps(matches, query) {
var handler = returnNull;
var props = {
ref: null,
params: null,
query: null,
activeRouteHandler: handler,
key: null
};
reversedArray(matches).forEach(function (match) {
var route = match.route;
props = Route.getUnreservedProps(route.props);
props.ref = '__activeRoute__';
props.params = match.params;
props.query = query;
props.activeRouteHandler = handler;
// TODO: Can we remove addHandlerKey?
if (route.props.addHandlerKey)
props.key = Path.injectParams(route.props.path, match.params);
handler = function (props, addedProps) {
if (arguments.length > 2 && typeof arguments[2] !== 'undefined')
throw new Error('Passing children to a route handler is not supported');
return route.props.handler(
copyProperties(props, addedProps)
);
}.bind(this, props);
});
return props;
}
var BrowserTransitionHandling = {
handleTransitionError: function (component, error) {
throw error; // This error probably originated in a transition hook.
},
handleAbortedTransition: function (component, transition) {
var reason = transition.abortReason;
if (reason instanceof Redirect) {
component.replaceWith(reason.to, reason.params, reason.query);
} else {
component.goBack();
}
}
};
var ServerTransitionHandling = {
handleTransitionError: function (component, error) {
// TODO
},
handleAbortedTransition: function (component, transition) {
// TODO
}
};
var TransitionHandling = canUseDOM ? BrowserTransitionHandling : ServerTransitionHandling;
var ActiveContext = require('../mixins/ActiveContext');
var LocationContext = require('../mixins/LocationContext');
var RouteContext = require('../mixins/RouteContext');
var ScrollContext = require('../mixins/ScrollContext');
/**
* The <Routes> component configures the route hierarchy and renders the

@@ -14,4 +312,203 @@ * route matching the current location when rendered into a document.

mixins: [ TransitionHandler ],
mixins: [ ActiveContext, LocationContext, RouteContext, ScrollContext ],
propTypes: {
initialPath: React.PropTypes.string,
onChange: React.PropTypes.func
},
getInitialState: function () {
return {
matches: []
};
},
componentWillMount: function () {
this.handlePathChange(this.props.initialPath);
},
componentDidMount: function () {
PathStore.addChangeListener(this.handlePathChange);
},
componentWillUnmount: function () {
PathStore.removeChangeListener(this.handlePathChange);
},
handlePathChange: function (_path) {
var path = _path || PathStore.getCurrentPath();
var actionType = PathStore.getCurrentActionType();
if (this.state.path === path)
return; // Nothing to do!
if (this.state.path)
this.recordScroll(this.state.path);
var self = this;
this.dispatch(path, function (error, transition) {
if (error) {
TransitionHandling.handleTransitionError(self, error);
} else if (transition.isAborted) {
TransitionHandling.handleAbortedTransition(self, transition);
} else {
self.updateScroll(path, actionType);
if (self.props.onChange) self.props.onChange.call(self);
}
});
},
/**
* Performs a depth-first search for the first route in the tree that matches on
* the given path. Returns an array of all routes in the tree leading to the one
* that matched in the format { route, params } where params is an object that
* contains the URL parameters relevant to that route. Returns null if no route
* in the tree matches the path.
*
* React.renderComponent(
* <Routes>
* <Route handler={App}>
* <Route name="posts" handler={Posts}/>
* <Route name="post" path="/posts/:id" handler={Post}/>
* </Route>
* </Routes>
* ).match('/posts/123'); => [ { route: <AppRoute>, params: {} },
* { route: <PostRoute>, params: { id: '123' } } ]
*/
match: function (path) {
return findMatches(Path.withoutQuery(path), this.getRoutes(), this.props.defaultRoute, this.props.notFoundRoute);
},
/**
* Performs a transition to the given path and calls callback(error, transition)
* with the Transition object when the transition is finished and the component's
* state has been updated accordingly.
*
* In a transition, the router first determines which routes are involved by
* beginning with the current route, up the route tree to the first parent route
* that is shared with the destination route, and back down the tree to the
* destination route. The willTransitionFrom hook is invoked on all route handlers
* we're transitioning away from, in reverse nesting order. Likewise, the
* willTransitionTo hook is invoked on all route handlers we're transitioning to.
*
* Both willTransitionFrom and willTransitionTo hooks may either abort or redirect
* the transition. To resolve asynchronously, they may use transition.wait(promise).
*/
dispatch: function (path, callback) {
var transition = new Transition(this, path);
var self = this;
computeNextState(this, transition, function (error, nextState) {
if (error || nextState == null)
return callback(error, transition);
self.setState(nextState, function () {
callback(null, transition);
});
});
},
/**
* Returns the props that should be used for the top-level route handler.
*/
getHandlerProps: function () {
return computeHandlerProps(this.state.matches, this.state.activeQuery);
},
/**
* Returns a reference to the active route handler's component instance.
*/
getActiveComponent: function () {
return this.refs.__activeRoute__;
},
/**
* Returns the current URL path.
*/
getCurrentPath: function () {
return this.state.path;
},
/**
* Returns an absolute URL path created from the given route
* name, URL parameters, and query values.
*/
makePath: function (to, params, query) {
var path;
if (Path.isAbsolute(to)) {
path = Path.normalize(to);
} else {
var namedRoutes = this.getNamedRoutes();
var route = namedRoutes[to];
invariant(
route,
'Unable to find a route named "' + to + '". Make sure you have ' +
'a <Route name="' + to + '"> defined somewhere in your <Routes>'
);
path = route.props.path;
}
return Path.withQuery(Path.injectParams(path, params), query);
},
/**
* Returns a string that may safely be used as the href of a
* link to the route with the given name.
*/
makeHref: function (to, params, query) {
var path = this.makePath(to, params, query);
if (this.getLocation() === HashLocation)
return '#' + path;
return path;
},
/**
* Transitions to the URL specified in the arguments by pushing
* a new URL onto the history stack.
*/
transitionTo: function (to, params, query) {
var location = this.getLocation();
invariant(
location,
'You cannot use transitionTo without a location'
);
location.push(this.makePath(to, params, query));
},
/**
* Transitions to the URL specified in the arguments by replacing
* the current URL in the history stack.
*/
replaceWith: function (to, params, query) {
var location = this.getLocation();
invariant(
location,
'You cannot use replaceWith without a location'
);
location.replace(this.makePath(to, params, query));
},
/**
* Transitions to the previous URL.
*/
goBack: function () {
var location = this.getLocation();
invariant(
location,
'You cannot use goBack without a location'
);
location.pop();
},
render: function () {

@@ -26,2 +523,22 @@ var match = this.state.matches[0];

);
},
childContextTypes: {
currentPath: React.PropTypes.string,
makePath: React.PropTypes.func.isRequired,
makeHref: React.PropTypes.func.isRequired,
transitionTo: React.PropTypes.func.isRequired,
replaceWith: React.PropTypes.func.isRequired,
goBack: React.PropTypes.func.isRequired
},
getChildContext: function () {
return {
currentPath: this.getCurrentPath(),
makePath: this.makePath,
makeHref: this.makeHref,
transitionTo: this.transitionTo,
replaceWith: this.replaceWith,
goBack: this.goBack
};
}

@@ -28,0 +545,0 @@

@@ -9,5 +9,3 @@ exports.DefaultRoute = require('./components/DefaultRoute');

exports.ActiveState = require('./mixins/ActiveState');
exports.AsyncState = require('./mixins/AsyncState');
exports.PathState = require('./mixins/PathState');
exports.RouteLookup = require('./mixins/RouteLookup');
exports.Transitions = require('./mixins/Transitions');
exports.CurrentPath = require('./mixins/CurrentPath');
exports.Navigation = require('./mixins/Navigation');

@@ -31,3 +31,4 @@ var invariant = require('react/lib/invariant');

// changed. It was probably caused by the user clicking the Back
// button, but may have also been the Forward button.
// button, but may have also been the Forward button or manual
// manipulation. So just guess 'pop'.
type: _actionType || LocationActions.POP,

@@ -34,0 +35,0 @@ path: getHashPath()

var React = require('react');
var ActiveDelegate = require('./ActiveDelegate');
/**
* A mixin for components that need to know about the routes, params,
* and query that are currently active. Components that use it get two
* things:
*
* 1. An `updateActiveState` method that is called when the
* active state changes.
* 2. An `isActive` method they can use to check if a route,
* params, and query are active.
*
* Example:
*
* var Tab = React.createClass({
*
* mixins: [ Router.ActiveState ],
*
* getInitialState: function () {
* return {
* isActive: false
* };
* },
*
* updateActiveState: function () {
* this.setState({
* isActive: this.isActive(routeName, params, query)
* })
* }
*
* });
* A mixin for components that need to know the routes, URL
* params and query that are currently active.
*/

@@ -37,29 +10,35 @@ var ActiveState = {

contextTypes: {
activeDelegate: React.PropTypes.any.isRequired
activeRoutes: React.PropTypes.array.isRequired,
activeParams: React.PropTypes.object.isRequired,
activeQuery: React.PropTypes.object.isRequired,
isActive: React.PropTypes.func.isRequired
},
componentWillMount: function () {
if (this.updateActiveState)
this.updateActiveState();
/**
* Returns an array of the routes that are currently active.
*/
getActiveRoutes: function () {
return this.context.activeRoutes;
},
componentDidMount: function () {
this.context.activeDelegate.addChangeListener(this.handleActiveStateChange);
/**
* Returns an object of the URL params that are currently active.
*/
getActiveParams: function () {
return this.context.activeParams;
},
componentWillUnmount: function () {
this.context.activeDelegate.removeChangeListener(this.handleActiveStateChange);
/**
* Returns an object of the query params that are currently active.
*/
getActiveQuery: function () {
return this.context.activeQuery;
},
handleActiveStateChange: function () {
if (this.isMounted() && this.updateActiveState)
this.updateActiveState();
},
/**
* Returns true if the route with the given name, URL parameters, and
* query are all currently active.
* A helper method to determine if a given route, params, and query
* are active.
*/
isActive: function (routeName, params, query) {
return this.context.activeDelegate.isActive(routeName, params, query);
isActive: function (to, params, query) {
return this.context.isActive(to, params, query);
}

@@ -66,0 +45,0 @@

@@ -8,2 +8,3 @@ var assert = require('assert');

describe('PathStore', function () {
beforeEach(function () {

@@ -101,2 +102,3 @@ LocationDispatcher.handleViewAction({

});
});

@@ -198,2 +198,8 @@ var expect = require('expect');

describe('when a pattern has one splat', function () {
it('returns the correct path', function () {
expect(Path.injectParams('/a/*/d', { splat: 'b/c' })).toEqual('/a/b/c/d');
});
});
describe('when a pattern has multiple splats', function () {

@@ -203,2 +209,8 @@ it('returns the correct path', function () {

});
it('complains if not given enough splat values', function () {
expect(function() {
Path.injectParams('/a/*/c/*', { splat: [ 'b' ] });
}).toThrow(Error);
});
});

@@ -205,0 +217,0 @@ });

@@ -11,4 +11,4 @@ var mixInto = require('react/lib/mixInto');

*/
function Transition(pathDelegate, path) {
this.pathDelegate = pathDelegate;
function Transition(routesComponent, path) {
this.routesComponent = routesComponent;
this.path = path;

@@ -35,3 +35,3 @@ this.abortReason = null;

retry: function () {
this.pathDelegate.replaceWith(this.path);
this.routesComponent.replaceWith(this.path);
}

@@ -38,0 +38,0 @@

{
"name": "react-router",
"version": "0.8.0",
"version": "0.9.0",
"description": "A complete routing library for React.js",

@@ -5,0 +5,0 @@ "main": "./modules/index",

@@ -5,8 +5,19 @@ require('./modules/components/__tests__/DefaultRoute-test');

require('./modules/components/__tests__/Routes-test');
require('./modules/mixins/__tests__/ActiveDelegate-test');
require('./modules/mixins/__tests__/AsyncState-test');
require('./modules/mixins/__tests__/PathDelegate-test');
require('./modules/mixins/__tests__/PathState-test');
require('./modules/mixins/__tests__/RouteContainer-test');
require('./modules/mixins/__tests__/ActiveContext-test');
require('./modules/mixins/__tests__/LocationContext-test');
require('./modules/mixins/__tests__/Navigation-test');
require('./modules/mixins/__tests__/RouteContext-test');
require('./modules/mixins/__tests__/ScrollContext-test');
require('./modules/stores/__tests__/PathStore-test');
require('./modules/utils/__tests__/Path-test');
var PathStore = require('./modules/stores/PathStore');
afterEach(function () {
// For some reason unmountComponentAtNode doesn't call componentWillUnmount :/
PathStore.removeAllChangeListeners();
});

@@ -8,3 +8,3 @@ Upgrade Guide

0.7.x -> 0.8.x
0.7.x -> 0.9.x
--------------

@@ -28,3 +28,3 @@

// 0.8.x
// 0.9.x
var SomethingActive = React.createClass({

@@ -41,3 +41,3 @@ mixins: [ActiveState],

### `<Routes onActiveStateChange/>` -> `PathState`
### `<Routes onActiveStateChange/>` -> `<Routes onChange />`

@@ -48,7 +48,16 @@ ```js

// 0.8.x
var App = React.createClass({
mixins: [PathState],
updatePath: fn
});
function fn(nextState) {}
// 0.9.x
<Routes onActiveStateChange={fn} />
function fn() {
// no arguments
// `this` is the routes instance
// here are some useful methods to get at the data you probably need
this.getCurrentPath();
this.getActiveRoutes();
this.getActiveParams();
this.getActiveQuery();
}
```

@@ -62,22 +71,4 @@

`.` used to be a delimiter like `/`, but now its a valid character in
your params. If you were using this feature you'll need to do the split
yourself.
your params.
```
// 0.7.x
var route = <Route path=":foo.:bar" />;
// 0.8.x
var route = <Route path=":foobar" handler={Handler}/>
Handler = React.createClass({
render: function() {
var split = this.props.params.foobar.split('.');
var foo = split[0];
var bar = split[1];
// ...
}
});
```
### `transition.retry()`

@@ -97,5 +88,5 @@

// 0.8.x
// 0.9.x
React.createClass({
mixins: [Transitions],
mixins: [Navigation],
login: function() {

@@ -123,3 +114,3 @@ // ...

// 0.8.x
// 0.9.x
React.createClass({

@@ -139,14 +130,14 @@ statics: {

There are now three scroll behaviors you can use:
- `'imitateBrowser'`
- `'browser'`
- `'scrollToTop'`
- `'none'`
`imitateBrowser` is the default, and imitates what browsers do in a
typical page reload scenario (preserves scroll positions when using the
back button, scrolls up when you come to a new page, etc.)
`browser` is the default, and imitates what browsers do in a typical
page reload scenario (preserves scroll positions when using the back
button, scrolls up when you come to a new page, etc.) Also, you can no
longer specify scroll behavior per `<Route/>` anymore, only `<Routes/>`
Also, you can't specify scroll behavior per `<Route/>` anymore.
```

@@ -160,3 +151,3 @@ <Routes scrollBehavior="scrollToTop"/>

It's gone now. We have made getting at the current routes incredibly
convenient now with the `RouteLookup` mixin.
convenient now with additions to the `ActiveState` mixin.

@@ -179,5 +170,7 @@ ### `Router.transitionTo, replaceWith, goBack`

// 0.8.x
// 0.9.x
var Navigation = Router.Navigation;
React.createClass({
mixins: [Router.Transitions],
mixins: [Navigation],
whenever: function() {

@@ -191,4 +184,12 @@ this.transitionTo('something');

0.7.x -> 0.8.x
--------------
Please don't upgrade to `0.8.0`, just skip to `0.9.x`.
`0.8.0` had some transient mixins we didn't intend to document, but had
some miscommunication :( If you were one of three people who used some
of these mixins and need help upgrading from `0.8.0 -> 0.9.x` find us on
freenode in `#rackt` or open a ticket. Thanks!
0.6.x -> 0.7.x

@@ -195,0 +196,0 @@ --------------

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc