首页 文章

自定义Redux中间件 - 发送到中间件链的开头?

提问于
浏览
2

我正在编写一个需要调度thunk动作的自定义中间件 . 问题是中间件是在中间件链中的 redux-thunk 之后调用的,所以当使用提供的 dispatch 时,我得到错误 Uncaught Error: Actions must be plain objects. Use custom middleware for async actions. .

export default function createMiddleware() {
    return ({dispatch, getState}) => next => (action) => {
        if(action.type !== 'FOO') {
            return next(action);
        }

        dispatch(thunkActionHere); // this is the issue
    }
}

我想将这个thunk动作发送回中间件链的开头,以便redux-thunk可以处理它 . 这可能吗?

更新:

function createMiddleware(extraArgument) {
    return function ({dispatch, getState}) {
        return function (next) {
            return function (action) {
                switch (action.type) {
                    case 'FOO1':
                        dispatch({type: 'NORMAL_ACTION'}); // works fine
                        break;
                    case 'FOO2':
                        dispatch(function() {
                            return (dispatch, getState) => { // Error: Actions must be plain objects. Use custom middleware for async actions.
                                console.log('inside the thunk');
                            };
                        });
                        break;
                    default:
                        return next(action);
                }
            };
        };
    };
}

const middleware = createMiddleware();
middleware.withExtraArgument = createMiddleware;

export default middleware;

这是我的商店配置:

export default function configureStore(initialState) {
    const store = createStore(rootReducer, initialState, compose(
        // Add other middleware on this line...
        applyMiddleware(bugsnagErrorCatcherMiddleware()),
        applyMiddleware(thunk.withExtraArgument({APIFactory, PusherManager})),
        applyMiddleware(webrtcVideoMiddleware.withExtraArgument(PusherManager)), // this is the middleware above
        applyMiddleware(bugsnagbreadcrumbLoggerMiddleware()),
        )
    );

    return store;
}

我不能把我的中间件放在redux-thunk之前,因为它不会收到thunk发送的动作 .

3 回答

  • 2

    在中间件链中调度会将操作发送到中间件链的开头,并像往常一样调用thunk(Demo - 查看控制台) .

    Why?

    原始 store.dispatch() (在应用中间件之前)检查操作是否是普通POJO,如果不是则抛出错误:

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

    applyMiddleware() dispatch 被一个新方法替换,这是一个中间件链,最后调用原始的 store.dispatch() . 你可以在applyMiddleware方法中看到它:

    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
        }
      }
    }
    

    顺便说一句 - 将您的中间件更改为此,因为第一个功能将阻止您的中间件工作 .

    export default const createMiddleware = ({dispatch, getState}) => next =>   (action) => {
        if(action.type !== 'FOO') {
            return next(action);
        }
    
        dispatch(thunkActionHere); // this is the issue
    }
    
  • 3

    事实证明问题出在我的商店配置中 . 使用redux的 compose 导致了这个问题 .

    之前:

    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())
        ));
    }
    

    后:

    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));
    }
    
  • 1

    我们使用redux-devtools-extension来自redux-devtools-extension . 与上述相同的问题和相同的解决方案 . 只需要使用 applyMiddleware(...middlewares) 而不是多个 applyMiddleware(middleware), applyMiddleware(middleware) 作为组合的参数 .

相关问题