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

时间:2017-02-14 03:28:47

标签: node.js reactjs redux react-redux redux-thunk

我正在编写一个需要调度thunk动作的自定义中间件。问题是中间件是在中间件链中的yytext[0]之后调用的,因此在使用提供的redux-thunk时出现错误Uncaught Error: Actions must be plain objects. Use custom middleware for async actions.

dispatch

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

更新

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

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

这是我的商店配置:

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;

我无法将我的中间件放在redux-thunk之前,因为它不会收到thunk发送的动作。

3 个答案:

答案 0 :(得分:4)

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

<强>为什么吗

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

答案 1 :(得分:2)

事实证明问题出在我的商店配置中。使用redux&#39; 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));
}

答案 2 :(得分:1)

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