自定义 Redux 中间件 - 调度到中间件链的开头?
Posted
技术标签:
【中文标题】自定义 Redux 中间件 - 调度到中间件链的开头?【英文标题】:Custom Redux Middleware - Dispatch to beginning of middleware chain? 【发布时间】:2017-07-02 05:23:10 【问题描述】:我正在编写一个需要调度 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 调度的操作。
【问题讨论】:
【参考方案1】:在中间件链内部调度会将动作发送到中间件链的开头,并像往常一样调用 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
【讨论】:
原来问题出在我的商店配置中。使用 redux 的compose
会导致问题。
您可以将其添加为另一个答案吗?这很有趣。
肯定的,刚刚做了【参考方案2】:
原来问题出在我的商店配置中。使用 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));
【讨论】:
【参考方案3】:我们使用来自redux-devtools-extension 的composeWithDevTools
。与上述相同的问题和相同的解决方案。只需要转而使用applyMiddleware(...middlewares)
而不是多个applyMiddleware(middleware), applyMiddleware(middleware)
作为组合的参数。
【讨论】:
以上是关于自定义 Redux 中间件 - 调度到中间件链的开头?的主要内容,如果未能解决你的问题,请参考以下文章
Redux 错误操作必须是普通对象。使用自定义中间件进行异步操作
React Redux - 动作必须是普通对象。使用自定义中间件进行异步操作
React 和 Redux:管理 Redux 自定义中间件列表