-
-
Notifications
You must be signed in to change notification settings - Fork 15.2k
Description
export default function applyMiddleware(...middlewares) {
return (next) => (reducer, initialState) => {
var store = next(reducer, initialState)
var dispatch = store.dispatch
var chain = []
var middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}Looking at the source code above, applyMiddleware creates a new store object and overrides dispatch with newly composed dispatch, and returns the new store.
There are shortcomings of this approach that I have experienced.
Take a look at the following scenario:
compose(
applyMiddleware(A),
reduxRouter(...),
applyMiddleware(B)
)(createStore)Because of the current implementation of applyMiddleware and compose to enhance createStore,
middleware A and middleware B will not share the same dispatch.
That means dispatch(action) in A and dispatch(action) in B will go through different code paths and may yield completely different outcomes, and it's confusing and difficult to debug.
Also, there is no way for middleware A to dispatch any reduxRouter actions such as pushState, replaceState, etc as those actions will never go through reduxRouter middleware.
My proposed solution would be:
- Create only one
storeinstance, and do not replace it bycomposeingcreateStore. - Enhance it by composing and replacing
dispatchfunction on the onlystoreinstance. - Share the
store, notdispatchacross all middlewares.
This way, there is only one universal store instance, and store.dispatch will always go through the same code path and yield the same outcome regardless of which middleware.