自定义 Redux 中间件 - 派发到中间件链的开头?
Custom Redux Middleware - Dispatch to beginning of middleware chain?
我正在编写一个需要分派 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 调度的操作。
中间件链内部的调度会将操作发送到中间件链的开头,并像往常一样调用 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
导致了这个问题。
之前:
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));
}
我们使用 redux-devtools-extension 中的 composeWithDevTools
。与上述相同的问题和相同的解决方案。只需要移动到使用 applyMiddleware(...middlewares)
而不是多个 applyMiddleware(middleware), applyMiddleware(middleware)
作为组合的参数。
我正在编写一个需要分派 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 调度的操作。
中间件链内部的调度会将操作发送到中间件链的开头,并像往常一样调用 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
导致了这个问题。
之前:
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));
}
我们使用 redux-devtools-extension 中的 composeWithDevTools
。与上述相同的问题和相同的解决方案。只需要移动到使用 applyMiddleware(...middlewares)
而不是多个 applyMiddleware(middleware), applyMiddleware(middleware)
作为组合的参数。