Custom Redux Middleware - Dispatch to beginning of middleware chain?

后端 未结 2 813
醉话见心
醉话见心 2021-01-20 08:01

I am writing a custom middleware that needs to dispatch thunk actions. The problem is that the middleware is called after redux-thunk in the middleware chain,

相关标签:
2条回答
  • 2021-01-20 08:28

    It turns out the issue was in my store configuration. Using redux's compose caused the issue.

    before:

    import {createStore, applyMiddleware, compose} from 'redux';
    import thunk from 'redux-thunk';
    import rootReducer from '../redux/reducers';
    import webrtcVideoMiddleware from '../redux/middleware/webrtcVideo';
    import bugsnagErrorCatcherMiddleware from '../redux/middleware/bugsnag/errorCatcher';
    import bugsnagbreadcrumbLoggerMiddleware from '../redux/middleware/bugsnag/breadcrumbLogger';
    import * as APIFactory from '../services/APIFactory';
    import Pusher from '../services/PusherManager';
    
    const PusherManager = new Pusher(false);
    
    export default function configureStore(initialState) {
        return createStore(rootReducer, initialState, compose(
            applyMiddleware(bugsnagErrorCatcherMiddleware()),
            applyMiddleware(thunk.withExtraArgument({APIFactory, PusherManager})),
            applyMiddleware(webrtcVideoMiddleware(PusherManager)),
            applyMiddleware(bugsnagbreadcrumbLoggerMiddleware())
        ));
    }
    

    after:

    import {createStore, applyMiddleware} from 'redux';
    import thunk from 'redux-thunk';
    import rootReducer from '../redux/reducers';
    import webRTCVideoMiddleware from '../redux/middleware/webrtcVideo';
    import bugsnagErrorCatcherMiddleware from '../redux/middleware/bugsnag/errorCatcher';
    import bugsnagBreadcrumbLoggerMiddleware from '../redux/middleware/bugsnag/breadcrumbLogger';
    import * as APIFactory from '../services/APIFactory';
    import Pusher from '../services/PusherManager';
    
    const PusherManager = new Pusher(false);
    
    export default function configureStore(initialState) {
        const middleware = [
            bugsnagErrorCatcherMiddleware(),
            thunk.withExtraArgument({APIFactory, PusherManager}),
            webRTCVideoMiddleware.withExtraArgument(PusherManager),
            bugsnagBreadcrumbLoggerMiddleware(),
        ];
    
        return createStore(rootReducer, initialState, applyMiddleware(...middleware));
    }
    
    0 讨论(0)
  • 2021-01-20 08:35

    Dispatching inside the middleware chain will send the action to the start of the middleware chain, and will call the thunk as usual (Demo - look at the console).

    Why?

    The original store.dispatch() (before applying middlewares) checks if the action is a plain POJO, and if not throws an error:

      function dispatch(action) {
        if (!isPlainObject(action)) {
          throw new Error(
            'Actions must be plain objects. ' +
            'Use custom middleware for async actions.'
          )
        }
    

    When you applyMiddleware() the dispatch is replaced by a new method, which is the chain of middleware, that call the original store.dispatch() in the end. You can see it in the applyMiddleware method:

    export default function applyMiddleware(...middlewares) {
      return (createStore) => (reducer, preloadedState, enhancer) => {
        const store = createStore(reducer, preloadedState, enhancer)
        let dispatch = store.dispatch // dispatch is now the original store's dispatch
        let chain = []
    
        const middlewareAPI = {
          getState: store.getState,
          dispatch: (action) => dispatch(action) // this refers to the dispatch variable. However, it's not the original dispatch, but the one that was created by compose
        }
        chain = middlewares.map(middleware => middleware(middlewareAPI))
        dispatch = compose(...chain)(store.dispatch) // dispatch is a composition of the chain, with the original dispatch in the end
    
        return {
          ...store,
          dispatch
        }
      }
    }
    

    btw - change your middleware to this, since the 1st function will prevent your middleware from working.

    export default const createMiddleware = ({dispatch, getState}) => next =>   (action) => {
        if(action.type !== 'FOO') {
            return next(action);
        }
    
        dispatch(thunkActionHere); // this is the issue
    }
    
    0 讨论(0)
提交回复
热议问题