It appears that each time a reducer is injected, all of the previous actions are replayed inside the respective reducers. The actions are not dispatched again, but the reducers will appear to run the actions. This doesn't appear to affect the store or state, but I'm surprised to see the replay. Can anyone speak to what is going on here?
I replicated this in the starter kit by adding two console.logs()
// src/store/reducers.js - line 11
export const injectReducer = (store, { key, reducer }) => {
console.log(`inject ${key}`)
store.asyncReducers[key] = reducer
store.replaceReducer(makeRootReducer(store.asyncReducers))
}
// src/routes/Counter/modules/counter.js - line 43
const ACTION_HANDLERS = {
[COUNTER_INCREMENT] : (state, action) => {
console.log('counter inc')
return state + action.payload
}
}
To view the replayed actions:
Here is the console output

Looks like the issue was logged here in the DevTools
https://github.com/gaearon/redux-devtools/issues/304
I thought I was going crazy the first time I came across this!!!
It may be an issue with the DevTools, but I think it's more exposed in a design flaw of how routes are created in the starter kit. Using Counter, here is a not so elegant way to address that - it would be helpful if the starter kit did this from the start as it does not look like a best practice to inject reducers every time getComponent() is called (which can be a LOT). If you are using a redux-saga it will multiply the events as they are accumulated, so an event will be replayed X times for X route visits:
import { injectReducer } from '../../store/reducers'
let counterReducersInjected = false
export default (store) => ({
path: 'counter',
getComponent (nextState, cb) {
require.ensure([], (require) => {
const Counter = require('./containers/CounterContainer').default
if (!counterReducersInjected) {
const reducer = require('./modules/counter').default
injectReducer(store, { key: 'counter', reducer })
counterReducersInjected = true
}
cb(null, Counter)
}, 'counter')
}
})
@justingreenberg - just wanted to also thank you at the same time for adding fractal design - it's great. maybe you already have a solution for this?
Yes, please see https://github.com/mxstbr/react-boilerplate/pull/976
I'm in jury duty on iPad but basically check reducer key in injectReducer
And you are very welcome 馃榾
thanks @justingreenberg - just posting here your fix, which goes in /src/store/reducers.js
// Line 12
if (Reflect.has(store.asyncReducers, key)) return;
Fixed with https://github.com/davezuko/react-redux-starter-kit/commit/820ca897db802979be2ce46e4d016076744145a2. Thanks @justingreenberg and @brianzinn .
This just bit me on a recent project. I was running into the issue of actions re-running after a route change because of injectReducer even though I'm using the latest starter-kit with the previous fix. Making this change helped me.
This has a minor annoying downside. If you make the change below, the initial state for reducers loaded through injectReducer is not immediately visible in the redux dev tools. Even though the initial state isn't visible in the "state" within the dev tools, it is actually in the app. The reducer will work correctly. The state for that reducer will be visible in the dev tools after the next action fires.
I suspect we should make this change in the starter-kit to prevent this from happening to others. Took me a while to track it down.
// src/store/createStore.js
// ...
if (__DEV__) {
const composeWithDevToolsExtension = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
if (typeof composeWithDevToolsExtension === 'function') {
composeEnhancers = composeWithDevToolsExtension({
shouldHotReload: false
})
}
}
//...
I have experienced this issue, even with the change https://github.com/davezuko/react-redux-starter-kit/commit/820ca897db802979be2ce46e4d016076744145a2. @heygrady is correct in that shouldHotReload needs setting to false, however I'm still having issues.
Essentially I'm having async issues, I've described over on the DevTools repo. To sum it up, my reducer isn't injecting synchronously for some reason.
injectReducer(store, { key: 'transactions', reducer })
console.log(store.getState().transactions) // undefined
setTimeout(() => {
console.log(store.getState().transactions) // yay we have stuff!
}, 100)
I'd love to hear anyone's thoughts on this! Thanks.
Most helpful comment
I thought I was going crazy the first time I came across this!!!
It may be an issue with the DevTools, but I think it's more exposed in a design flaw of how routes are created in the starter kit. Using Counter, here is a not so elegant way to address that - it would be helpful if the starter kit did this from the start as it does not look like a best practice to inject reducers every time getComponent() is called (which can be a LOT). If you are using a redux-saga it will multiply the events as they are accumulated, so an event will be replayed X times for X route visits:
@justingreenberg - just wanted to also thank you at the same time for adding fractal design - it's great. maybe you already have a solution for this?