diff --git a/src/BaseLink.js b/src/BaseLink.js
index 30526212..58bdc0cf 100644
--- a/src/BaseLink.js
+++ b/src/BaseLink.js
@@ -1,6 +1,8 @@
import React from 'react';
import elementType from 'react-prop-types/lib/elementType';
+import { routerShape } from './PropTypes';
+
const propTypes = {
Component: elementType.isRequired,
to: React.PropTypes.oneOfType([
@@ -14,19 +16,10 @@ const propTypes = {
exact: React.PropTypes.bool.isRequired,
target: React.PropTypes.string,
onClick: React.PropTypes.func,
- push: React.PropTypes.func.isRequired,
};
const contextTypes = {
- store: React.PropTypes.shape({
- farce: React.PropTypes.shape({
- createHref: React.PropTypes.func.isRequired,
- createLocation: React.PropTypes.func.isRequired,
- }).isRequired,
- found: React.PropTypes.shape({
- isActive: React.PropTypes.func.isRequired,
- }).isRequired,
- }).isRequired,
+ router: routerShape.isRequired,
};
const defaultProps = {
@@ -36,7 +29,7 @@ const defaultProps = {
class BaseLink extends React.Component {
onClick = (event) => {
- const { onClick, target, push, to } = this.props;
+ const { onClick, target, to } = this.props;
if (onClick) {
onClick(event);
@@ -62,7 +55,7 @@ class BaseLink extends React.Component {
// FIXME: When clicking on a link to the same location in the browser, the
// actual becomes a replace rather than a push. We may want the same
// handling – perhaps implemented in the Farce protocol.
- push(to);
+ this.context.router.push(to);
};
render() {
@@ -77,13 +70,11 @@ class BaseLink extends React.Component {
...props
} = this.props;
- const { farce, found } = this.context.store;
-
- delete props.push; // Used in onClick.
+ const { router } = this.context;
if (activeClassName || activeStyle || activePropName) {
- const toLocation = farce.createLocation(to);
- const active = found.isActive(toLocation, match, { exact });
+ const toLocation = router.createLocation(to);
+ const active = router.isActive(toLocation, match, { exact });
if (active) {
if (activeClassName) {
@@ -104,7 +95,7 @@ class BaseLink extends React.Component {
return (
);
diff --git a/src/PropTypes.js b/src/PropTypes.js
new file mode 100644
index 00000000..aa535c84
--- /dev/null
+++ b/src/PropTypes.js
@@ -0,0 +1,11 @@
+import React from 'react';
+
+export const routerShape = React.PropTypes.shape({
+ push: React.PropTypes.func.isRequired,
+ replace: React.PropTypes.func.isRequired,
+ go: React.PropTypes.func.isRequired,
+
+ createHref: React.PropTypes.func.isRequired,
+ createLocation: React.PropTypes.func.isRequired,
+ isActive: React.PropTypes.func.isRequired,
+});
diff --git a/src/createBaseRouter.js b/src/createBaseRouter.js
index a1ff8469..2d021733 100644
--- a/src/createBaseRouter.js
+++ b/src/createBaseRouter.js
@@ -2,6 +2,7 @@ import React from 'react';
import getRoutes from './getRoutes';
import HttpError from './HttpError';
+import { routerShape } from './PropTypes';
import RedirectException from './RedirectException';
export default function createBaseRouter({ routeConfig, matcher }) {
@@ -10,10 +11,19 @@ export default function createBaseRouter({ routeConfig, matcher }) {
matchContext: React.PropTypes.any, // eslint-disable-line react/no-unused-prop-types
resolveElements: React.PropTypes.func.isRequired, // eslint-disable-line react/no-unused-prop-types
render: React.PropTypes.func.isRequired,
- replace: React.PropTypes.func.isRequired, // eslint-disable-line react/no-unused-prop-types
+ push: React.PropTypes.func.isRequired,
+ replace: React.PropTypes.func.isRequired,
+ go: React.PropTypes.func.isRequired,
+ createHref: React.PropTypes.func.isRequired,
+ createLocation: React.PropTypes.func.isRequired,
+ isActive: React.PropTypes.func.isRequired,
initialState: React.PropTypes.object,
};
+ const childContextTypes = {
+ router: routerShape.isRequired,
+ };
+
class BaseRouter extends React.Component {
constructor(props, context) {
super(props, context);
@@ -31,6 +41,28 @@ export default function createBaseRouter({ routeConfig, matcher }) {
}
}
+ getChildContext() {
+ const {
+ push,
+ replace,
+ go,
+ createHref,
+ createLocation,
+ isActive,
+ } = this.props;
+
+ return {
+ router: {
+ push,
+ replace,
+ go,
+ createHref,
+ createLocation,
+ isActive,
+ },
+ };
+ }
+
componentWillReceiveProps(nextProps) {
++this.matchIndex;
this.resolveMatch(nextProps);
@@ -94,6 +126,7 @@ export default function createBaseRouter({ routeConfig, matcher }) {
}
BaseRouter.propTypes = propTypes;
+ BaseRouter.childContextTypes = childContextTypes;
return BaseRouter;
}
diff --git a/src/createConnectedLink.js b/src/createConnectedLink.js
index a7ef8c6a..7214baa5 100644
--- a/src/createConnectedLink.js
+++ b/src/createConnectedLink.js
@@ -1,4 +1,3 @@
-import FarceActions from 'farce/lib/Actions';
import { connect } from 'react-redux';
import BaseLink from './BaseLink';
@@ -8,6 +7,11 @@ export default function createConnectedLink({
}) {
return connect(
state => ({ match: getMatch(state) }),
- { push: FarceActions.push },
+ null,
+ (stateProps, dispatchProps, ownProps) => ({
+ ...ownProps,
+ ...stateProps,
+ // We don't want dispatch here.
+ }),
)(BaseLink);
}
diff --git a/src/createConnectedRouter.js b/src/createConnectedRouter.js
index ad0ca6e3..b2a9920f 100644
--- a/src/createConnectedRouter.js
+++ b/src/createConnectedRouter.js
@@ -1,4 +1,5 @@
import FarceActions from 'farce/lib/Actions';
+import React from 'react';
import { connect } from 'react-redux';
import createBaseRouter from './createBaseRouter';
@@ -7,8 +8,39 @@ export default function createConnectedRouter({
getMatch = ({ match }) => match,
...options
}) {
- return connect(
+ // createHref, createLocation, and isActive are taken directly from the store
+ // to avoid potential issues with middlewares in the chain messing with the
+ // return value from dispatch.
+ const propTypes = {
+ store: React.PropTypes.shape({
+ farce: React.PropTypes.shape({
+ createHref: React.PropTypes.func.isRequired,
+ createLocation: React.PropTypes.func.isRequired,
+ }).isRequired,
+ found: React.PropTypes.shape({
+ isActive: React.PropTypes.func.isRequired,
+ }).isRequired,
+ }).isRequired,
+ };
+
+ const ConnectedRouter = connect(
state => ({ match: getMatch(state) }),
- { replace: FarceActions.replace },
+ {
+ push: FarceActions.push,
+ replace: FarceActions.replace,
+ go: FarceActions.go,
+ },
+ (stateProps, dispatchProps, { store, ...ownProps }) => ({
+ ...ownProps,
+ ...stateProps,
+ ...dispatchProps,
+ createHref: store.farce.createHref,
+ createLocation: store.farce.createLocation,
+ isActive: store.found.isActive,
+ }),
)(createBaseRouter(options));
+
+ ConnectedRouter.propTypes = propTypes;
+
+ return ConnectedRouter;
}
diff --git a/src/createFarceRouter.js b/src/createFarceRouter.js
index 85ba0ec2..01bc02ea 100644
--- a/src/createFarceRouter.js
+++ b/src/createFarceRouter.js
@@ -35,10 +35,17 @@ export default function createFarceRouter({
this.store.dispatch(FarceActions.init());
}
+ componentWillUnmount() {
+ this.store.dispatch(FarceActions.dispose());
+ }
+
render() {
return (
-
+
);
}
diff --git a/src/index.js b/src/index.js
index 77fdaf09..791fed23 100644
--- a/src/index.js
+++ b/src/index.js
@@ -14,6 +14,7 @@ export HttpError from './HttpError';
export Link from './Link';
export Matcher from './Matcher';
export matchReducer from './matchReducer';
+export { routerShape } from './PropTypes';
export Redirect from './Redirect';
export RedirectException from './RedirectException';
export resolveElements from './resolveElements';