Skip to content

Shortcomings of the current applyMiddleware and composing createStore. #1051

@joonhocho

Description

@joonhocho
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:

  1. Create only one store instance, and do not replace it by composeing createStore.
  2. Enhance it by composing and replacing dispatch function on the only store instance.
  3. Share the store, not dispatch across 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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions