Skip to content
This repository was archived by the owner on Oct 26, 2018. It is now read-only.

Split history syncing from action creators #259

Merged
merged 38 commits into from
Feb 17, 2016
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
7cdc72a
Extract the action creators and middleware.
timdorr Feb 5, 2016
2946938
Pull in @gaearon's history syncer.
timdorr Feb 5, 2016
21b511b
Go with Babel Stage 1 for export extensions.
timdorr Feb 5, 2016
fbe174f
Update the example against the new API.
timdorr Feb 5, 2016
88bc0de
Minor reorg. Split the reducer to its own file.
timdorr Feb 5, 2016
a2ec57b
Fix linting in example.
timdorr Feb 5, 2016
36845f5
Hollow out the old tests and fill in something basic.
timdorr Feb 5, 2016
10e34d2
Update build script for multi-file setup.
timdorr Feb 5, 2016
4c17b0e
Clean up some testing framework stuff.
timdorr Feb 5, 2016
c61c92c
Use spread instead Object.assign. Following #259
webmasterkai Feb 6, 2016
4afd74f
Confirm location change before listener invokation
webmasterkai Feb 6, 2016
fc12f70
Begin updating example in README. Re #259
webmasterkai Feb 6, 2016
1a8c800
README syncHistory becomes syncHistoryWithStore
webmasterkai Feb 6, 2016
08b3818
Merge pull request #262 from webmasterkai/synchronicity
timdorr Feb 6, 2016
379dc9c
Add back route action tests.
timdorr Feb 6, 2016
def06ff
Add reducer tests. Ensure actions are FSA.
timdorr Feb 6, 2016
2b177f5
Add middleware test. Reorg test suite.
timdorr Feb 6, 2016
2c88667
rename UPDATE_LOCATION to CALL_HISTORY_METHOD
webmasterkai Feb 6, 2016
a452791
Merge pull request #265 from webmasterkai/actionTypeAlt
timdorr Feb 6, 2016
cb9d6b6
Redone docs.
timdorr Feb 7, 2016
50faa62
Add a note warning about reading from store.
timdorr Feb 7, 2016
4308208
Add back some other relevant tests.
timdorr Feb 7, 2016
0b7b322
Add docs for syncHistoryWithStore's options.
timdorr Feb 8, 2016
9b2dc36
Don't leave test side effects with history singltons.
timdorr Feb 8, 2016
820f46e
4.0.0-beta.1
timdorr Feb 8, 2016
8ed23ca
Merge remote-tracking branch 'origin/master' into synchronicity
timdorr Feb 10, 2016
98ceca0
Merge remote-tracking branch 'origin/master' into synchronicity
timdorr Feb 12, 2016
f0e09db
docs(readme): add example migration from ^3.0.0
Feb 12, 2016
d54496c
Merge pull request #271 from davezuko/patch-2
timdorr Feb 12, 2016
d45f1c6
Update README.md
justin808 Feb 14, 2016
60fa049
Merge pull request #278 from justin808/patch-2
timdorr Feb 14, 2016
8bc27b9
Merge remote-tracking branch 'origin/master' into synchronicity
timdorr Feb 15, 2016
cdc941d
Switch to run example on webpack-dev-server.
timdorr Feb 15, 2016
31b3809
added example to readme
svrcekmichal Feb 15, 2016
d2e5173
Merge pull request #280 from svrcekmichal/synchronicity
timdorr Feb 15, 2016
67aefaa
Merge remote-tracking branch 'origin/master' into synchronicity
timdorr Feb 15, 2016
87402ea
Add a SSR example.
timdorr Feb 17, 2016
8d4750e
Merge remote-tracking branch 'origin/master' into synchronicity
timdorr Feb 17, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .babelrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"presets": ["es2015", "stage-2"]
"presets": ["es2015", "stage-1"]
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
lib
node_modules
coverage
*.log
20 changes: 8 additions & 12 deletions examples/basic/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,17 @@ import DockMonitor from 'redux-devtools-dock-monitor'

import React from 'react'
import ReactDOM from 'react-dom'
import { applyMiddleware, compose, createStore, combineReducers } from 'redux'
import { createStore, combineReducers } from 'redux'
import { Provider } from 'react-redux'
import { Router, Route, IndexRoute } from 'react-router'
import createHistory from 'history/lib/createHashHistory'
import { syncHistory, routeReducer } from 'react-router-redux'
import { Router, Route, IndexRoute, hashHistory } from 'react-router'
import { syncHistoryWithStore, routerReducer } from 'react-router-redux'

import * as reducers from './reducers'
import { App, Home, Foo, Bar } from './components'

const history = createHistory()
const middleware = syncHistory(history)
const reducer = combineReducers({
...reducers,
routing: routeReducer
routing: routerReducer
})

const DevTools = createDevTools(
Expand All @@ -27,12 +24,11 @@ const DevTools = createDevTools(
</DockMonitor>
)

const finalCreateStore = compose(
applyMiddleware(middleware),
const store = createStore(
reducer,
DevTools.instrument()
)(createStore)
const store = finalCreateStore(reducer)
middleware.listenForReplays(store)
)
const history = syncHistoryWithStore(hashHistory, store)

ReactDOM.render(
<Provider store={store}>
Expand Down
13 changes: 3 additions & 10 deletions examples/basic/components/App.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import React from 'react'
import { Link } from 'react-router'
import { connect } from 'react-redux'
import { routeActions } from 'react-router-redux'
import { Link, hashHistory } from 'react-router'

function App({ push, children }) {
export default function App({ children }) {
return (
<div>
<header>
Expand All @@ -16,14 +14,9 @@ function App({ push, children }) {
<Link to="/bar">Bar</Link>
</header>
<div>
<button onClick={() => push('/foo')}>Go to /foo</button>
<button onClick={() => hashHistory.push('/foo')}>Go to /foo</button>
</div>
<div style={{ marginTop: '1.5em' }}>{children}</div>
</div>
)
}

export default connect(
null,
routeActions
)(App)
31 changes: 15 additions & 16 deletions examples/basic/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,27 @@
"repository": "rackt/react-router-redux",
"license": "MIT",
"dependencies": {
"history": "^1.14.0",
"react": "^0.14.2",
"react-dom": "^0.14.2",
"react-redux": "^4.0.0",
"react-router": "^1.0.0",
"redux": "^3.0.4",
"react-router-redux": "^2.1.0"
"react": "^0.14.7",
"react-dom": "^0.14.7",
"react-redux": "^4.3.0",
"react-router": "^2.0.0-rc5",
"redux": "^3.2.1",
"react-router-redux": "^3.0.0"
},
"devDependencies": {
"babel-core": "^6.1.21",
"babel-eslint": "^5.0.0-beta6",
"babel-loader": "^6.2.0",
"babel-preset-es2015": "^6.1.18",
"babel-preset-react": "^6.1.18",
"babel-core": "^6.4.5",
"babel-eslint": "^5.0.0-beta9",
"babel-loader": "^6.2.2",
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-preset-stage-1": "^6.3.13",
"eslint": "^1.10.3",
"eslint-config-rackt": "^1.1.1",
"eslint-plugin-react": "^3.15.0",
"redux-devtools": "^3.0.0",
"eslint-plugin-react": "^3.16.1",
"redux-devtools": "^3.1.0",
"redux-devtools-dock-monitor": "^1.0.1",
"redux-devtools-log-monitor": "^1.0.1",
"webpack": "^1.12.6"
"redux-devtools-log-monitor": "^1.0.4",
"webpack": "^1.12.13"
},
"scripts": {
"start": "webpack --watch"
Expand Down
29 changes: 16 additions & 13 deletions karma.conf.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
var path = require('path');
var webpack = require('webpack');
'use strict'

const path = require('path')

module.exports = function (config) {

var runCoverage = process.env.COVERAGE === 'true';
let runCoverage = process.env.COVERAGE === 'true'

var coverageLoaders = [];
var coverageReporters = [];
let coverageLoaders = []
let coverageReporters = []

if (runCoverage) {
coverageLoaders.push({
Expand All @@ -15,7 +16,7 @@ module.exports = function (config) {
loader: 'isparta'
}),

coverageReporters.push('coverage');
coverageReporters.push('coverage')
}

config.set({
Expand All @@ -39,12 +40,14 @@ module.exports = function (config) {
module: {
preLoaders: [
{
loader: 'babel',
test: /\.js$/,
exclude: [
path.resolve('node_modules/')
],
loader: 'babel'
},
include: [
path.resolve('src/'),
path.resolve('test/')
]

}
].concat(coverageLoaders)
}
},
Expand All @@ -59,5 +62,5 @@ module.exports = function (config) {
{ type: 'json', subdir: 'browser-coverage', file: 'coverage.json' }
]
}
});
};
})
}
12 changes: 7 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@
],
"license": "MIT",
"scripts": {
"build": "mkdir -p lib && babel ./src/index.js --out-file ./lib/index.js",
"build": "mkdir -p lib && babel ./src -d lib",
"lint": "eslint examples src test",
"test": "npm run lint && npm run test:node && npm run test:browser",
"test:node": "mocha --compilers js:babel-core/register --recursive ./test/node",
"test:node": "mocha --compilers js:babel-register --recursive ./test/node",
"test:browser": "karma start",
"test:cov": "npm run test:cov:browser && npm run test:cov:node && npm run test:cov:report",
"test:cov:node": "babel-node $(npm bin)/isparta cover $(npm bin)/_mocha report --dir ./coverage/node-coverage -- --recursive ./test/node",
Expand All @@ -41,13 +41,14 @@
"babel-core": "^6.2.1",
"babel-eslint": "^4.1.6",
"babel-loader": "^6.2.0",
"babel-preset-es2015": "^6.1.2",
"babel-preset-stage-2": "^6.3.13",
"babel-polyfill": "^6.3.14",
"babel-preset-es2015": "^6.3.13",
"babel-preset-stage-1": "^6.3.13",
"babel-register": "^6.4.3",
"eslint": "^1.10.3",
"eslint-config-rackt": "^1.1.1",
"eslint-plugin-react": "^3.15.0",
"expect": "^1.13.0",
"history": "^1.14.0",
"isparta": "^4.0.0",
"isparta-loader": "^2.0.0",
"karma": "^0.13.3",
Expand All @@ -62,6 +63,7 @@
"karma-webpack": "^1.7.0",
"mocha": "^2.3.4",
"react": "^0.14.3",
"react-router": "^2.0.0-rc5",
"redux": "^3.0.4",
"redux-devtools": "^3.0.0",
"redux-devtools-dock-monitor": "^1.0.1",
Expand Down
26 changes: 26 additions & 0 deletions src/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* This action type will be dispatched by the history actions below.
* If you're writing a middleware to watch for navigation events, be sure to
* look for actions of this type.
*/
export const UPDATE_LOCATION = '@@router/UPDATE_LOCATION'

function updateLocation(method) {
return (...args) => ({
type: UPDATE_LOCATION,
payload: { method, args }
})
}

/**
* These actions correspond to the history API.
* The associated routerMiddleware will capture these events before they get to
* your reducer and reissue them as the matching function on your history.
*/
export const push = updateLocation('push')
export const replace = updateLocation('replace')
export const go = updateLocation('go')
export const goBack = updateLocation('goBack')
export const goForward = updateLocation('goForward')

export const routeActions = { push, replace, go, goBack, goForward }
121 changes: 9 additions & 112 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,112 +1,9 @@
// Constants

export const TRANSITION = '@@router/TRANSITION'
export const UPDATE_LOCATION = '@@router/UPDATE_LOCATION'

const SELECT_LOCATION = state => state.routing.location

function transition(method) {
return (...args) => ({
type: TRANSITION,
payload: { method, args }
})
}

export const push = transition('push')
export const replace = transition('replace')
export const go = transition('go')
export const goBack = transition('goBack')
export const goForward = transition('goForward')

export const routeActions = { push, replace, go, goBack, goForward }

function updateLocation(location) {
return {
type: UPDATE_LOCATION,
payload: location
}
}

// Reducer

const initialState = {
location: undefined
}

export function routeReducer(state = initialState, { type, payload: location }) {
if (type !== UPDATE_LOCATION) {
return state
}

return { ...state, location }
}

// Syncing

export function syncHistory(history) {
let unsubscribeHistory, currentKey, unsubscribeStore
let connected = false, syncing = false

history.listen(location => { initialState.location = location })()

function middleware(store) {
unsubscribeHistory = history.listen(location => {
currentKey = location.key
if (syncing) {
// Don't dispatch a new action if we're replaying location.
return
}

store.dispatch(updateLocation(location))
})

connected = true

return next => action => {
if (action.type !== TRANSITION || !connected) {
return next(action)
}

const { payload: { method, args } } = action
history[method](...args)
}
}

middleware.listenForReplays =
(store, selectLocationState = SELECT_LOCATION) => {
const getLocationState = () => selectLocationState(store.getState())
const initialLocation = getLocationState()

unsubscribeStore = store.subscribe(() => {
const location = getLocationState()

// If we're resetting to the beginning, use the saved initial value. We
// need to dispatch a new action at this point to populate the store
// appropriately.
if (location.key === initialLocation.key) {
history.replace(initialLocation)
return
}

// Otherwise, if we need to update the history location, do so without
// dispatching a new action, as we're just bringing history in sync
// with the store.
if (location.key !== currentKey) {
syncing = true
history.transitionTo(location)
syncing = false
}
})
}

middleware.unsubscribe = () => {
unsubscribeHistory()
if (unsubscribeStore) {
unsubscribeStore()
}

connected = false
}

return middleware
}
export syncHistoryWithStore from './sync'
export { LOCATION_CHANGE, routerReducer } from './reducer'

export {
UPDATE_LOCATION,
push, replace, go, goBack, goForward,
routeActions
} from './actions'
export routerMiddleware from './middleware'
17 changes: 17 additions & 0 deletions src/middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import UPDATE_LOCATION from './actions'

/**
* This middleware captures UPDATE_LOCATION actions to redirect to the
* provided history object. This will prevent these actions from reaching your
* reducer or any middleware that comes after this one.
*/
export default function routerMiddleware(history) {
return () => next => action => {
if (action.type !== UPDATE_LOCATION) {
return next(action)
}

const { payload: { method, args } } = action
history[method](...args)
}
}
Loading