Skip to content

Commit

Permalink
ConnectedRouter is ready.
Browse files Browse the repository at this point in the history
  • Loading branch information
supasate committed Dec 12, 2016
1 parent 4ba3860 commit 1d1be9d
Show file tree
Hide file tree
Showing 7 changed files with 2,800 additions and 0 deletions.
60 changes: 60 additions & 0 deletions src/ConnectedRouter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import { StaticRouter } from 'react-router'
import { onLocationChanged } from './actions'

/*
* ConnectedRouter listens to a history object passed from props.
* When history is changed, it dispatches action to redux store.
* Then, store will pass props to component to render.
* This creates uni-directional flow from history->store->router->components.
*/
class ConnectedRouter extends Component {
constructor(props) {
super(props)

this.unlisten = props.history.listen((location, action) => {
props.onLocationChanged(location, action)
})
}

componentWillUnmount() {
this.unlisten()
}

render() {
const { history, basename, children } = this.props

return (
<StaticRouter
action={history.action}
location={history.location}
basename={basename}
onPush={history.push}
onReplace={history.replace}
blockTransitions={history.block}
>
{ children }
</StaticRouter>
)
}
}

ConnectedRouter.propTypes = {
history: PropTypes.object.isRequired,
location: PropTypes.oneOfType([ PropTypes.object, PropTypes.string ]),
basename: PropTypes.string,
children: PropTypes.oneOfType([ PropTypes.func, PropTypes.node ]),
onLocationChanged: PropTypes.func.isRequired,
}

const mapStateToProps = state => ({
action: state.router.action,
location: state.router.location,
})

const mapDispatchToProps = dispatch => ({
onLocationChanged: (location, action) => dispatch(onLocationChanged(location, action))
})

export default connect(mapStateToProps, mapDispatchToProps)(ConnectedRouter)
43 changes: 43 additions & 0 deletions src/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* This action type will be dispatched when your history
* receives a location change.
*/
export const LOCATION_CHANGE = '@@router/LOCATION_CHANGE'

export const onLocationChanged = (location, action) => ({
type: LOCATION_CHANGE,
payload: {
location,
action,
}
})

/**
* 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 CALL_HISTORY_METHOD = '@@router/CALL_HISTORY_METHOD'

const updateLocation = (method) => {
return (...args) => ({
type: CALL_HISTORY_METHOD,
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 routerActions = { push, replace, go, goBack, goForward }
12 changes: 12 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export syncHistoryWithStore from './sync'
export { LOCATION_CHANGE } from './actions'
export routerReducer from './reducer'

export {
CALL_HISTORY_METHOD,
push, replace, go, goBack, goForward,
routerActions
} from './actions'

export routerMiddleware from './middleware'
export ConnectedRouter from './ConnectedRouter'
18 changes: 18 additions & 0 deletions src/middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { CALL_HISTORY_METHOD } from './actions'

/**
* This middleware captures CALL_HISTORY_METHOD 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.
*/
const routerMiddleware = (history) => () => next => action => {
if (action.type !== CALL_HISTORY_METHOD) {
return next(action)
}

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


export default routerMiddleware
22 changes: 22 additions & 0 deletions src/reducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { LOCATION_CHANGE } from './actions'

/**
* This reducer will update the state with the most recent location history
* has transitioned to.
*/
const initialState = {
location: null
}

const routerReducer = (state = initialState, { type, payload } = {}) => {
if (type === LOCATION_CHANGE) {
return {
...state,
...payload,
}
}

return state
}

export default routerReducer
16 changes: 16 additions & 0 deletions src/sync.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { onLocationChanged } from './actions'

/**
* This function synchronizes your history state with the Redux store.
* Location changes flow from history to the store. An unsubscribe function
* is returned if you ever what the store to stop being in sync with the state
*/
const syncHistoryWithStore = (history, store) => {
// Whenever location changes, dispatch an action to get it in the store
return history.listen((location, action) => {
// Tell the store to update by dispatching an action
store.dispatch(onLocationChanged(location, action))
})
}

export default syncHistoryWithStore
Loading

0 comments on commit 1d1be9d

Please sign in to comment.