diff --git a/src/ConnectedRouter.js b/src/ConnectedRouter.js
index a3032038..c1a1da5c 100644
--- a/src/ConnectedRouter.js
+++ b/src/ConnectedRouter.js
@@ -1,11 +1,12 @@
-import React, { Component } from 'react'
+import React, {PureComponent} from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { Router } from 'react-router'
import { onLocationChanged } from './actions'
+import createSelectors from "./selectors"
const createConnectedRouter = (structure) => {
- const { getIn, toJS } = structure
+ const { getLocation } = createSelectors(structure)
/*
* ConnectedRouter listens to a history object passed from props.
* When history is changed, it dispatches action to redux store.
@@ -13,20 +14,26 @@ const createConnectedRouter = (structure) => {
* This creates uni-directional flow from history->store->router->components.
*/
- class ConnectedRouter extends Component {
+ class ConnectedRouter extends PureComponent {
+
+ constructor(props, context) {
+ super(props)
+ this.passedContext = context
+ }
+
componentDidMount() {
- const { store, history, onLocationChanged } = this.props
+ const { history, onLocationChanged } = this.props
this.inTimeTravelling = false
- // Subscribe to store changes to check if we are in time travelling
- this.unsubscribe = store.subscribe(() => {
+ // Subscribe to store changes
+ this.unsubscribe = this.passedContext.store.subscribe(() => {
// Extract store's location
const {
pathname: pathnameInStore,
search: searchInStore,
hash: hashInStore,
- } = toJS(getIn(store.getState(), ['router', 'location']))
+ } = getLocation(this.passedContext.store.getState())
// Extract history's location
const {
pathname: pathnameInHistory,
@@ -89,26 +96,16 @@ const createConnectedRouter = (structure) => {
location: PropTypes.object.isRequired,
push: PropTypes.func.isRequired,
}).isRequired,
- location: PropTypes.oneOfType([
- PropTypes.object,
- PropTypes.string,
- ]).isRequired,
- action: PropTypes.string.isRequired,
basename: PropTypes.string,
children: PropTypes.oneOfType([ PropTypes.func, PropTypes.node ]),
onLocationChanged: PropTypes.func.isRequired,
}
- const mapStateToProps = state => ({
- action: getIn(state, ['router', 'action']),
- location: getIn(state, ['router', 'location']),
- })
-
const mapDispatchToProps = dispatch => ({
onLocationChanged: (location, action) => dispatch(onLocationChanged(location, action))
})
- return connect(mapStateToProps, mapDispatchToProps)(ConnectedRouter)
+ return connect(null, mapDispatchToProps)(ConnectedRouter)
}
export default createConnectedRouter
diff --git a/test/ConnectedRouter.test.js b/test/ConnectedRouter.test.js
index f722f67a..8662c2fb 100644
--- a/test/ConnectedRouter.test.js
+++ b/test/ConnectedRouter.test.js
@@ -2,7 +2,7 @@ import 'raf/polyfill'
import React, { Children, Component } from 'react'
import PropTypes from 'prop-types'
import configureStore from 'redux-mock-store'
-import { createStore, combineReducers } from 'redux'
+import {createStore, combineReducers, compose, applyMiddleware} from 'redux'
import { ActionCreators, instrument } from 'redux-devtools'
import Enzyme from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'
@@ -14,6 +14,7 @@ import plainStructure from '../src/structure/plain'
import immutableStructure from '../src/structure/immutable'
import seamlessImmutableStructure from '../src/structure/seamless-immutable'
import { connectRouter, ConnectedRouter } from '../src'
+import routerMiddleware from "../src/middleware"
Enzyme.configure({ adapter: new Adapter() })
@@ -151,6 +152,118 @@ describe('ConnectedRouter', () => {
expect(onLocationChangedSpy.mock.calls).toHaveLength(1)
})
+
+ it('only renders one time when mounted', () => {
+ let renderCount = 0
+
+ const RenderCounter = () => {
+ renderCount++
+ return null
+ }
+
+ mount(
+
+
+
+
+
+ )
+
+ expect(renderCount).toBe(1)
+ })
+
+ it('does not render again when non-related action is fired', () => {
+ // Initialize the render counter variable
+ let renderCount = 0
+
+ // Create redux store with router state
+ store = createStore(
+ combineReducers({
+ incrementReducer: (state = 0, action = {}) => {
+ if (action.type === 'testAction')
+ return ++state
+
+ return state
+ },
+ router: connectRouter(history)
+ }),
+ compose(applyMiddleware(routerMiddleware(history)))
+ )
+
+
+ const RenderCounter = () => {
+ renderCount++
+ return null
+ }
+
+ mount(
+
+
+
+
+
+ )
+
+ store.dispatch({ type: 'testAction' })
+ history.push('/new-location')
+ expect(renderCount).toBe(2)
+ })
+
+ it('only renders one time when mounted', () => {
+ let renderCount = 0
+
+ const RenderCounter = () => {
+ renderCount++
+ return null
+ }
+
+ mount(
+
+
+
+
+
+ )
+
+ expect(renderCount).toBe(1)
+ })
+
+ it('does not render again when non-related action is fired', () => {
+ // Initialize the render counter variable
+ let renderCount = 0
+
+ // Create redux store with router state
+ store = createStore(
+ combineReducers({
+ incrementReducer: (state = 0, action = {}) => {
+ if (action.type === 'testAction')
+ return ++state
+
+ return state
+ },
+ router: connectRouter(history)
+ }),
+ compose(applyMiddleware(routerMiddleware(history)))
+ )
+
+
+ const RenderCounter = () => {
+ renderCount++
+ return null
+ }
+
+ mount(
+
+
+
+
+
+ )
+
+ store.dispatch({ type: 'testAction' })
+ history.push('/new-location')
+ expect(renderCount).toBe(2)
+ })
})
describe('with seamless immutable structure', () => {
@@ -198,6 +311,62 @@ describe('ConnectedRouter', () => {
expect(onLocationChangedSpy.mock.calls).toHaveLength(1)
})
+
+ it('only renders one time when mounted', () => {
+ let renderCount = 0
+
+ const RenderCounter = () => {
+ renderCount++
+ return null
+ }
+
+ mount(
+
+
+
+
+
+ )
+
+ expect(renderCount).toBe(1)
+ })
+
+ it('does not render again when non-related action is fired', () => {
+ // Initialize the render counter variable
+ let renderCount = 0
+
+ // Create redux store with router state
+ store = createStore(
+ combineReducers({
+ incrementReducer: (state = 0, action = {}) => {
+ if (action.type === 'testAction')
+ return ++state
+
+ return state
+ },
+ router: connectRouter(history)
+ }),
+ compose(applyMiddleware(routerMiddleware(history)))
+ )
+
+
+ const RenderCounter = () => {
+ renderCount++
+ return null
+ }
+
+ mount(
+
+
+
+
+
+ )
+
+ store.dispatch({ type: 'testAction' })
+ history.push('/new-location')
+ expect(renderCount).toBe(2)
+ })
})
describe('Redux DevTools', () => {