From 497f3f37875e60880470ed4bd41da528331819f4 Mon Sep 17 00:00:00 2001 From: Ville Saukkonen Date: Sat, 29 Jul 2017 02:19:14 +0300 Subject: [PATCH 1/3] Matcher is not needed in store --- src/enhancer.js | 5 +---- test/enhancer.spec.js | 4 ---- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/enhancer.js b/src/enhancer.js index 05da172f..d4c39465 100644 --- a/src/enhancer.js +++ b/src/enhancer.js @@ -76,8 +76,5 @@ export default ({ history, matchRoute, createMatcher }: EnhancerArgs) => ( ); }); - return { - ...store, - matchRoute - }; + return { ...store }; }; diff --git a/test/enhancer.spec.js b/test/enhancer.spec.js index 409a3946..b9c45816 100644 --- a/test/enhancer.spec.js +++ b/test/enhancer.spec.js @@ -49,10 +49,6 @@ describe('Router store enhancer', () => { expect(store.dispatch).to.be.calledOnce; }); - it('attaches the matcher to the store', () => { - expect(store).to.have.property('matchRoute'); - }); - it('replaces routes', () => { store.dispatch({ type: REPLACE_ROUTES, From 3e530c055a5a4cb17af1b33c939bc4d2f4a6caec Mon Sep 17 00:00:00 2001 From: Ville Saukkonen Date: Sat, 29 Jul 2017 02:50:37 +0300 Subject: [PATCH 2/3] update enhancer interface --- demo/client/app.js | 3 +-- demo/server/index.js | 4 ++-- src/enhancer.js | 19 +++---------------- test/enhancer.spec.js | 9 ++++----- test/environment/browser-router.spec.js | 8 +++++--- test/environment/express-router.spec.js | 8 +++++--- test/environment/hapi-router.spec.js | 5 +++-- test/environment/hash-router.spec.js | 8 +++++--- 8 files changed, 28 insertions(+), 36 deletions(-) diff --git a/demo/client/app.js b/demo/client/app.js index 4d564918..21d2a80c 100644 --- a/demo/client/app.js +++ b/demo/client/app.js @@ -18,12 +18,11 @@ const store = createStore( // initial state the hbs template inserted window.__INITIAL_STATE || {}, compose( - enhancer, applyMiddleware(middleware), window.devToolsExtension ? window.devToolsExtension() : f => f ) ); - +enhancer(store); const initialLocation = store.getState().router; if (initialLocation) { store.dispatch(initializeCurrentLocation(initialLocation)); diff --git a/demo/server/index.js b/demo/server/index.js index 498614cc..49811bcb 100644 --- a/demo/server/index.js +++ b/demo/server/index.js @@ -33,7 +33,6 @@ const Root = require('../client/demo').default; const createStore = redux.createStore; const combineReducers = redux.combineReducers; -const compose = redux.compose; const applyMiddleware = redux.applyMiddleware; const PORT = 4567; @@ -83,9 +82,10 @@ app.get('/*', (req, res) => { const store = createStore( combineReducers({ router: router.reducer }), initialState, - compose(router.enhancer, applyMiddleware(router.middleware)) + applyMiddleware(router.middleware) ); + router.enhancer(store); const content = renderToString(wrap(store)(Root)); return res.send( diff --git a/src/enhancer.js b/src/enhancer.js index d4c39465..f08d7568 100644 --- a/src/enhancer.js +++ b/src/enhancer.js @@ -1,10 +1,8 @@ // @flow -import type { StoreCreator, Reducer, StoreEnhancer } from 'redux'; +import type { Store } from 'redux'; import type { History } from 'history'; -import type { Location } from './types'; - import qs from 'query-string'; import { POP } from './types'; @@ -12,26 +10,17 @@ import { locationDidChange, didReplaceRoutes, replace } from './actions'; import matchCache from './util/match-cache'; -type InitialState = { - router: Location -}; - type EnhancerArgs = {| history: History, matchRoute: Function, createMatcher: Function |}; + export default ({ history, matchRoute, createMatcher }: EnhancerArgs) => ( - createStore: StoreCreator<*, *> -) => ( - userReducer: Reducer<*, *>, - initialState: InitialState, - enhancer: StoreEnhancer<*, *> + store: Store<*, *> ) => { let currentMatcher = matchRoute; - const store = createStore(userReducer, initialState, enhancer); - // Replace the matcher when replacing routes store.subscribe(() => { const { @@ -75,6 +64,4 @@ export default ({ history, matchRoute, createMatcher }: EnhancerArgs) => ( }) ); }); - - return { ...store }; }; diff --git a/test/enhancer.spec.js b/test/enhancer.spec.js index b9c45816..6d0e4b52 100644 --- a/test/enhancer.spec.js +++ b/test/enhancer.spec.js @@ -1,7 +1,7 @@ import chai, { expect } from 'chai'; import sinonChai from 'sinon-chai'; -import { combineReducers, compose, createStore, applyMiddleware } from 'redux'; +import { combineReducers, createStore, applyMiddleware } from 'redux'; import { PUSH, REPLACE_ROUTES } from '../src/types'; import install from '../src/install'; @@ -33,8 +33,9 @@ describe('Router store enhancer', () => { store = createStore( combineReducers({ router: reducer }), {}, - compose(enhancer, applyMiddleware(middleware)) + applyMiddleware(middleware) ); + enhancer(store); sandbox.spy(store, 'dispatch'); }); @@ -60,9 +61,7 @@ describe('Router store enhancer', () => { } }); - // This dispatch isn't the dispatch used in the enhancer - // (each enhancer has its own copy of dispatch) - expect(store.dispatch).to.be.calledOnce; + expect(store.dispatch).to.be.calledThrice; expect(historyStub.replace).to.be.calledOnce; expect(listenStub).to.be.calledOnce; diff --git a/test/environment/browser-router.spec.js b/test/environment/browser-router.spec.js index def8edc1..35b0bad7 100644 --- a/test/environment/browser-router.spec.js +++ b/test/environment/browser-router.spec.js @@ -1,7 +1,7 @@ import chai, { expect } from 'chai'; import sinonChai from 'sinon-chai'; -import { applyMiddleware, combineReducers, createStore, compose } from 'redux'; +import { applyMiddleware, combineReducers, createStore } from 'redux'; import routerForBrowser from '../../src/environment/browser-router'; @@ -25,8 +25,9 @@ describe('Browser router', () => { const store = createStore( combineReducers({ router: reducer }), {}, - compose(enhancer, applyMiddleware(middleware)) + applyMiddleware(middleware) ); + enhancer(store); const state = store.getState(); expect(state).to.have.nested.property('router.pathname', '/home'); expect(state).to.have.nested.property('router.search', '?get=schwifty'); @@ -52,8 +53,9 @@ describe('Browser router', () => { const store = createStore( combineReducers({ router: reducer }), {}, - compose(enhancer, applyMiddleware(middleware)) + applyMiddleware(middleware) ); + enhancer(store); const state = store.getState(); expect(state).to.have.nested.property('router.basename', '/cob-planet'); expect(state).to.have.nested.property('router.pathname', '/home'); diff --git a/test/environment/express-router.spec.js b/test/environment/express-router.spec.js index 0c3a97ea..dc2f602c 100644 --- a/test/environment/express-router.spec.js +++ b/test/environment/express-router.spec.js @@ -1,7 +1,7 @@ import chai, { expect } from 'chai'; import sinonChai from 'sinon-chai'; -import { applyMiddleware, combineReducers, createStore, compose } from 'redux'; +import { applyMiddleware, combineReducers, createStore } from 'redux'; import routerForExpress from '../../src/environment/express-router'; @@ -21,8 +21,9 @@ describe('Express router', () => { const store = createStore( combineReducers({ router: reducer }), {}, - compose(enhancer, applyMiddleware(middleware)) + applyMiddleware(middleware) ); + enhancer(store); const state = store.getState(); expect(state).to.have.nested.property('router.pathname', '/home'); expect(state).to.have.nested.property('router.search', '?get=schwifty'); @@ -43,8 +44,9 @@ describe('Express router', () => { const store = createStore( combineReducers({ router: reducer }), {}, - compose(enhancer, applyMiddleware(middleware)) + applyMiddleware(middleware) ); + enhancer(store); const state = store.getState(); expect(state).to.have.nested.property('router.basename', '/cob-planet'); expect(state).to.have.nested.property('router.pathname', '/home'); diff --git a/test/environment/hapi-router.spec.js b/test/environment/hapi-router.spec.js index a5a3f4d3..d1a3cb63 100644 --- a/test/environment/hapi-router.spec.js +++ b/test/environment/hapi-router.spec.js @@ -1,7 +1,7 @@ import chai, { expect } from 'chai'; import sinonChai from 'sinon-chai'; -import { applyMiddleware, combineReducers, createStore, compose } from 'redux'; +import { applyMiddleware, combineReducers, createStore } from 'redux'; import routerForHapi from '../../src/environment/hapi-router'; @@ -21,8 +21,9 @@ describe('Hapi router', () => { const store = createStore( combineReducers({ router: reducer }), {}, - compose(enhancer, applyMiddleware(middleware)) + applyMiddleware(middleware) ); + enhancer(store); const state = store.getState(); expect(state).to.have.nested.property('router.pathname', '/home'); expect(state).to.have.nested.property('router.search', '?get=schwifty'); diff --git a/test/environment/hash-router.spec.js b/test/environment/hash-router.spec.js index 1ba96335..bdff3d33 100644 --- a/test/environment/hash-router.spec.js +++ b/test/environment/hash-router.spec.js @@ -1,7 +1,7 @@ import chai, { expect } from 'chai'; import sinonChai from 'sinon-chai'; -import { applyMiddleware, combineReducers, createStore, compose } from 'redux'; +import { applyMiddleware, combineReducers, createStore } from 'redux'; import routerForHash from '../../src/environment/hash-router'; @@ -25,8 +25,9 @@ describe('Hash router', () => { const store = createStore( combineReducers({ router: reducer }), {}, - compose(enhancer, applyMiddleware(middleware)) + applyMiddleware(middleware) ); + enhancer(store); const state = store.getState(); expect(state).to.have.nested.property('router.pathname', '/home'); expect(state).to.have.nested.property('router.search', '?get=schwifty'); @@ -52,8 +53,9 @@ describe('Hash router', () => { const store = createStore( combineReducers({ router: reducer }), {}, - compose(enhancer, applyMiddleware(middleware)) + applyMiddleware(middleware) ); + enhancer(store); const state = store.getState(); expect(state).to.have.nested.property('router.basename', '/cob-planet'); expect(state).to.have.nested.property('router.pathname', '/home'); From 8d9bf2fe22d90cbda61fadf030d67b9ff09f8e5c Mon Sep 17 00:00:00 2001 From: Ville Saukkonen Date: Sat, 29 Jul 2017 03:06:04 +0300 Subject: [PATCH 3/3] rename router.enhancer to router.connect --- ADVANCED.md | 18 ++++++++++++------ README.md | 14 ++++++++------ demo/client/app.js | 4 ++-- demo/server/index.js | 2 +- src/{enhancer.js => connector.js} | 4 ++-- src/install.js | 4 ++-- test/{enhancer.spec.js => connector.spec.js} | 6 +++--- test/environment/browser-router.spec.js | 10 +++++----- test/environment/express-router.spec.js | 10 +++++----- test/environment/hapi-router.spec.js | 6 +++--- test/environment/hash-router.spec.js | 10 +++++----- 11 files changed, 48 insertions(+), 40 deletions(-) rename src/{enhancer.js => connector.js} (93%) rename test/{enhancer.spec.js => connector.spec.js} (93%) diff --git a/ADVANCED.md b/ADVANCED.md index 0d4abcb6..76eb2bdc 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -37,7 +37,7 @@ app.use('/*', (req, res) => { // routerForExpress will infer the basename // from req.baseUrl! // - const { reducer, middleware, enhancer } = routerForExpress({ + const { reducer, middleware, connect } = routerForExpress({ routes, request: req }) @@ -45,8 +45,10 @@ app.use('/*', (req, res) => { const store = createStore( combineReducers({ router: reducer }), { what: 'ever' }, - compose(enhancer, applyMiddleware(middleware)) + applyMiddleware(middleware) ); + + connect(store); // ...then renderToString() your components as usual, // passing your new store to your component. @@ -86,7 +88,7 @@ server.route({ // Create the Redux store, passing in the Hapi // request to the routerForHapi factory. - const { reducer, middleware, enhancer } = routerForHapi({ + const { reducer, middleware, connect } = routerForHapi({ routes, request }) @@ -94,8 +96,10 @@ server.route({ const store = createStore( reducer, { what: 'ever' }, - compose(enhancer, applyMiddleware(middleware)) + applyMiddleware(middleware) ); + + connect(store); // ...then renderToString() your components as usual, // passing your new store to your component. @@ -125,16 +129,18 @@ const routes = { const { reducer, - enhancer, + connect, middleware } = routerForBrowser({ routes }); const store = createStore( combineReducers({ router: reducer }), window.__INITIAL_STATE, - compose(enhancer, applyMiddleware(middleware)) + applyMiddleware(middleware) ); +connect(store); + // ...then render() your components as usual, // passing your new store to your component. ``` diff --git a/README.md b/README.md index d07992eb..9de81368 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ While React Router is a great, well-supported library, it hoards URL state withi ## Redux usage -To hook into Redux applications, `redux-little-router` uses a store enhancer that wraps the `history` module and adds current and previous router state to your store. The enhancer listens for location changes and dispatches rich actions containing the URL, parameters, and any custom data assigned to the route. `redux-little-router` also adds a middleware that intercepts navigation actions and calls their equivalent method in `history`. +To connect into Redux applications, `redux-little-router` uses a store connector that wraps the `history` module and adds current and previous router state to your store. The connector listens for location changes and dispatches rich actions containing the URL, parameters, and any custom data assigned to the route. `redux-little-router` also adds a middleware that intercepts navigation actions and calls their equivalent method in `history`. ### Wiring up the boilerplate @@ -72,11 +72,11 @@ const routes = { // Install the router into the store for a browser-only environment. // routerForBrowser is a factory method that returns a store -// enhancer and a middleware. +// connect and a middleware. const { reducer, middleware, - enhancer + connect } = routerForBrowser({ // The configured routes. Required. routes, @@ -87,11 +87,13 @@ const { const clientOnlyStore = createStore( combineReducers({ router: reducer, yourReducer }), initialState, - compose(enhancer, applyMiddleware(middleware)) + applyMiddleware(middleware) ); + +connect(clientOnlyStore); ``` -Often, you'll want to update state or trigger side effects after loading the initial URL. To maintain compatibility with other store enhancers (particularly ones that handle side effects, like `redux-loop` or `redux-saga`), we require this optional initial dispatch to happen in client code by doing the following: +Often, you'll want to update state or trigger side effects after loading the initial URL. To maintain compatibility with store enhancers (particularly ones that handle side effects, like `redux-loop` or `redux-saga`), we require this optional initial dispatch to happen in client code by doing the following: ```js import { initializeCurrentLocation } from 'redux-little-router'; @@ -164,7 +166,7 @@ export const redirect = href => dispatch => { }; ``` -On location changes, the store enhancer dispatches a `LOCATION_CHANGED` action that contains at least the following properties: +On location changes, the router connector dispatches a `LOCATION_CHANGED` action that contains at least the following properties: ```js // For a URL matching /messages/:user diff --git a/demo/client/app.js b/demo/client/app.js index 21d2a80c..b716f9de 100644 --- a/demo/client/app.js +++ b/demo/client/app.js @@ -10,7 +10,7 @@ import routes from './routes'; import wrap from './wrap'; import Demo from './demo'; -const { reducer, enhancer, middleware } = routerForBrowser({ routes }); +const { reducer, connect, middleware } = routerForBrowser({ routes }); const store = createStore( combineReducers({ router: reducer }), @@ -22,7 +22,7 @@ const store = createStore( window.devToolsExtension ? window.devToolsExtension() : f => f ) ); -enhancer(store); +connect(store); const initialLocation = store.getState().router; if (initialLocation) { store.dispatch(initializeCurrentLocation(initialLocation)); diff --git a/demo/server/index.js b/demo/server/index.js index 49811bcb..37f8ce98 100644 --- a/demo/server/index.js +++ b/demo/server/index.js @@ -85,7 +85,7 @@ app.get('/*', (req, res) => { applyMiddleware(router.middleware) ); - router.enhancer(store); + router.connect(store); const content = renderToString(wrap(store)(Root)); return res.send( diff --git a/src/enhancer.js b/src/connector.js similarity index 93% rename from src/enhancer.js rename to src/connector.js index f08d7568..df37a3e0 100644 --- a/src/enhancer.js +++ b/src/connector.js @@ -10,13 +10,13 @@ import { locationDidChange, didReplaceRoutes, replace } from './actions'; import matchCache from './util/match-cache'; -type EnhancerArgs = {| +type ConnectorArgs = {| history: History, matchRoute: Function, createMatcher: Function |}; -export default ({ history, matchRoute, createMatcher }: EnhancerArgs) => ( +export default ({ history, matchRoute, createMatcher }: ConnectorArgs) => ( store: Store<*, *> ) => { let currentMatcher = matchRoute; diff --git a/src/install.js b/src/install.js index 9014daf0..50deae0c 100644 --- a/src/install.js +++ b/src/install.js @@ -4,7 +4,7 @@ import type { Location } from './types'; import reducer from './reducer'; import middleware from './middleware'; -import enhancer from './enhancer'; +import connector from './connector'; import { default as matcherFactory } from './util/create-matcher'; import validateRoutes from './util/validate-routes'; @@ -36,7 +36,7 @@ export default ({ } }), middleware: middleware({ history }), - enhancer: enhancer({ + connect: connector({ history, matchRoute, createMatcher diff --git a/test/enhancer.spec.js b/test/connector.spec.js similarity index 93% rename from test/enhancer.spec.js rename to test/connector.spec.js index 6d0e4b52..29eb9a69 100644 --- a/test/enhancer.spec.js +++ b/test/connector.spec.js @@ -11,7 +11,7 @@ import defaultRoutes from './test-util/fixtures/routes'; chai.use(sinonChai); -describe('Router store enhancer', () => { +describe('Router store connector', () => { let store; let historyStub; let listenStub; @@ -24,7 +24,7 @@ describe('Router store enhancer', () => { const replace = sandbox.spy(() => listen(listenStub)); historyStub = { push, replace, listen }; - const { reducer, middleware, enhancer } = install({ + const { reducer, middleware, connect } = install({ routes: defaultRoutes, history: historyStub, location: { pathname: '/' } @@ -35,7 +35,7 @@ describe('Router store enhancer', () => { {}, applyMiddleware(middleware) ); - enhancer(store); + connect(store); sandbox.spy(store, 'dispatch'); }); diff --git a/test/environment/browser-router.spec.js b/test/environment/browser-router.spec.js index 35b0bad7..fcea108a 100644 --- a/test/environment/browser-router.spec.js +++ b/test/environment/browser-router.spec.js @@ -10,8 +10,8 @@ import routes from '../test-util/fixtures/routes'; chai.use(sinonChai); describe('Browser router', () => { - it('creates a browser store enhancer using history location', () => { - const { enhancer, middleware, reducer } = routerForBrowser({ + it('creates a browser store connector using history location', () => { + const { connect, middleware, reducer } = routerForBrowser({ routes, history: { location: { @@ -27,7 +27,7 @@ describe('Browser router', () => { {}, applyMiddleware(middleware) ); - enhancer(store); + connect(store); const state = store.getState(); expect(state).to.have.nested.property('router.pathname', '/home'); expect(state).to.have.nested.property('router.search', '?get=schwifty'); @@ -38,7 +38,7 @@ describe('Browser router', () => { }); it('supports basenames', () => { - const { enhancer, middleware, reducer } = routerForBrowser({ + const { connect, middleware, reducer } = routerForBrowser({ routes, basename: '/cob-planet', history: { @@ -55,7 +55,7 @@ describe('Browser router', () => { {}, applyMiddleware(middleware) ); - enhancer(store); + connect(store); const state = store.getState(); expect(state).to.have.nested.property('router.basename', '/cob-planet'); expect(state).to.have.nested.property('router.pathname', '/home'); diff --git a/test/environment/express-router.spec.js b/test/environment/express-router.spec.js index dc2f602c..80f08b96 100644 --- a/test/environment/express-router.spec.js +++ b/test/environment/express-router.spec.js @@ -10,8 +10,8 @@ import routes from '../test-util/fixtures/routes'; chai.use(sinonChai); describe('Express router', () => { - it('creates a server store enhancer using Express request object', () => { - const { enhancer, middleware, reducer } = routerForExpress({ + it('creates a server store connector using Express request object', () => { + const { connect, middleware, reducer } = routerForExpress({ routes, request: { path: '/home', @@ -23,7 +23,7 @@ describe('Express router', () => { {}, applyMiddleware(middleware) ); - enhancer(store); + connect(store); const state = store.getState(); expect(state).to.have.nested.property('router.pathname', '/home'); expect(state).to.have.nested.property('router.search', '?get=schwifty'); @@ -33,7 +33,7 @@ describe('Express router', () => { }); it('supports basenames', () => { - const { enhancer, middleware, reducer } = routerForExpress({ + const { connect, middleware, reducer } = routerForExpress({ routes, request: { baseUrl: '/cob-planet', @@ -46,7 +46,7 @@ describe('Express router', () => { {}, applyMiddleware(middleware) ); - enhancer(store); + connect(store); const state = store.getState(); expect(state).to.have.nested.property('router.basename', '/cob-planet'); expect(state).to.have.nested.property('router.pathname', '/home'); diff --git a/test/environment/hapi-router.spec.js b/test/environment/hapi-router.spec.js index d1a3cb63..acf42cd7 100644 --- a/test/environment/hapi-router.spec.js +++ b/test/environment/hapi-router.spec.js @@ -10,8 +10,8 @@ import routes from '../test-util/fixtures/routes'; chai.use(sinonChai); describe('Hapi router', () => { - it('creates a server store enhancer using Hapi request object', () => { - const { enhancer, middleware, reducer } = routerForHapi({ + it('creates a server store connector using Hapi request object', () => { + const { connect, middleware, reducer } = routerForHapi({ routes, request: { path: '/home', @@ -23,7 +23,7 @@ describe('Hapi router', () => { {}, applyMiddleware(middleware) ); - enhancer(store); + connect(store); const state = store.getState(); expect(state).to.have.nested.property('router.pathname', '/home'); expect(state).to.have.nested.property('router.search', '?get=schwifty'); diff --git a/test/environment/hash-router.spec.js b/test/environment/hash-router.spec.js index bdff3d33..bf4d2c91 100644 --- a/test/environment/hash-router.spec.js +++ b/test/environment/hash-router.spec.js @@ -10,7 +10,7 @@ import routes from '../test-util/fixtures/routes'; chai.use(sinonChai); describe('Hash router', () => { - it('creates a browser store enhancer using window.location', () => { + it('creates a browser store connector using window.location', () => { const history = { listen() {}, location: { @@ -18,7 +18,7 @@ describe('Hash router', () => { search: '?get=schwifty' } }; - const { enhancer, middleware, reducer } = routerForHash({ + const { connect, middleware, reducer } = routerForHash({ routes, history }); @@ -27,7 +27,7 @@ describe('Hash router', () => { {}, applyMiddleware(middleware) ); - enhancer(store); + connect(store); const state = store.getState(); expect(state).to.have.nested.property('router.pathname', '/home'); expect(state).to.have.nested.property('router.search', '?get=schwifty'); @@ -45,7 +45,7 @@ describe('Hash router', () => { } }; - const { enhancer, middleware, reducer } = routerForHash({ + const { connect, middleware, reducer } = routerForHash({ routes, history, basename: '/cob-planet' @@ -55,7 +55,7 @@ describe('Hash router', () => { {}, applyMiddleware(middleware) ); - enhancer(store); + connect(store); const state = store.getState(); expect(state).to.have.nested.property('router.basename', '/cob-planet'); expect(state).to.have.nested.property('router.pathname', '/home');