我一直遇到似乎是并行的异步redux错误。在较高的层面上,我试图发送一堆从网址获取资产的异步操作。
我最初尝试redux-thunk
中间件并遇到了同样的错误。我已经转移到一个“听众”风格的中间件,看起来像:
// listener-middleware.js
// taken from https://medium.com/@alexandereardon/the-middleware-listener-pattern-better-asynchronous-actions-in-redux-16164fb6186f
export default (...listeners) => store => next => action => {
// listeners are provided with a picture
// of the world before the action is applied
const preActionState = store.getState();
// release the action to reducers before
// firing additional actions
next(action);
// always async
setTimeout(() => {
// can have multiple listeners listening
// against the same action.type
listeners.forEach(listener => {
if (listener[action.type]) {
listener[action.type](action, store.dispatch, preActionState);
}
});
});
};
资产监听器如下所示:
import { assetsFetchBinary, assetsReceiveBinary } from '../assets/actions.js';
export default {
[assetsFetchBinary.toString()]: (action, dispatch, state) => {
const assetPath = state.config.assetPath + '/' + action.payload.assetName;
if(!state.assets[action.payload.assetName]){
fetch(assetPath)
.then(response => response.arrayBuffer())
.then(arrayBuffer => {
return dispatch(assetsReceiveBinary(action.payload.assetName, arrayBuffer))
});
}
},
}
商店配置和applyMiddleware
是:
import { createStore, applyMiddleware } from 'minidux'
import rootReducer from './rootReducer';
import { createLogger } from 'redux-logger';
import { routerMiddleware } from 'react-router-redux'
import { sceneTickAction } from './dsl/scene/actions.js';
import assetMiddleware from './dsl/assets/middleware.js';
import listenerMiddleware from './listener-middleware';
const logger = (initialState) => {
const predicate = (getState, action) => {
if(action.type === sceneTickAction.toString()){
return false;
}
return true;
}
return createLogger({predicate});
};
const getMiddleWare = (initialState, history) => {
const list = [];
if(initialState.system.role === 'client'){
const routerHistory = routerMiddleware(history);
list.push(routerHistory);
list.push(listenerMiddleware(assetMiddleware))
list.push(logger(initialState));
return list;
};
const configureStore = (initialState, history) => {
return createStore(
rootReducer(initialState),
initialState,
applyMiddleware.apply(this, getMiddleWare(initialState, history))
);
}
export default configureStore
行动是直截了当的
import { createAction } from 'redux-actions';
export const assetsReceiveBinary = createAction('@ASSETS/RECEIVE_BINARY')
export const assetsFetchBinary = createAction('@ASSETS/FETCH_BINARY')
和减速器:
import { handleActions } from 'redux-actions';
import { assetsFetchBinary, assetsReceiveBinary } from '../assets/actions.js';
export const assetReducer = (state, action) => handleActions({
[assetsFetchBinary]: (state, action) => {
if(state.assets[action.path]){ return state; }
return {
...state,
assets: {
...state.assets,
[action.path]: {}
}
}
},
[assetsReceiveBinary]: (state, action) => {
return {
...state,
assets: {
...state.assets,
[action.path]: {arrayBuffer: action.arrayBuffer}
}
}
}
}, state)(state, action);
export default assetReducer;
堆栈跟踪如下:
createStore.js:27 Uncaught Error: modifiers may not emit actions
at dispatch (createStore.js:27)
at listener-middleware.js:10
at middleware.js:13
at Object.assetsFetchBinary (bindActionCreators.js:3)
at CharacterView.jsx:21
at Array.forEach (<anonymous>)
at CharacterView.componentDidUpdate (CharacterView.jsx:20)
at commitLifeCycles (react-dom.development.js:11517)
at commitAllLifeCycles (react-dom.development.js:12294)
at HTMLUnknownElement.callCallback (react-dom.development.js:1299)
at Object.invokeGuardedCallbackDev (react-dom.development.js:1338)
at invokeGuardedCallback (react-dom.development.js:1195)
at commitAllWork (react-dom.development.js:12415)
at workLoop (react-dom.development.js:12687)
at HTMLUnknownElement.callCallback (react-dom.development.js:1299)
at Object.invokeGuardedCallbackDev (react-dom.development.js:1338)
at invokeGuardedCallback (react-dom.development.js:1195)
at performWork (react-dom.development.js:12800)
at scheduleUpdateImpl (react-dom.development.js:13185)
at scheduleUpdate (react-dom.development.js:13124)
at Object.enqueueSetState (react-dom.development.js:9646)
at Connect../node_modules/react/cjs/react.development.js.ReactComponent.setState (react.development.js:
调度线27看起来像:
function dispatch (action) {
if (!action || !isPlainObject(action)) throw new Error('action parameter is required and must be a plain object')
if (!action.type || typeof action.type !== 'string') throw new Error('type property of action is required and must be a string')
if (isEmitting) throw new Error('modifiers may not emit actions')
该调度电话的action
看起来像{type: "@ASSETS/FETCH_BINARY"}
此外,我在componentDidMount
中调用这些操作,如下所示:
class CharacterView extends Component {
componentDidUpdate(){
if(this.props.missingItemAssets){
this.props.missingItemAssets.forEach((assetName) => {
this.props.assetsFetchBinary(assetName);
});
}
}
如果我将调度电话打包在0
setTimeout
中,则会阻止错误。
class CharacterView extends Component {
componentDidUpdate(){
if(this.props.missingItemAssets){
setTimeout(() => {
this.props.missingItemAssets.forEach((assetName) => {
this.props.assetsFetchBinary(assetName);
});
}, 0)
}
}
答案 0 :(得分:2)
您提到您在componentDidMount
中发送了操作,但您的示例显示您实际上是从componentDidUpdate
发送的。
class CharacterView extends Component {
componentDidUpdate(){ // <--- should be componentDidMount?
if(this.props.missingItemAssets){
this.props.missingItemAssets.forEach((assetName) => {
this.props.assetsFetchBinary(assetName);
});
}
}
}
这可能会导致您的问题,假设您的问题中的示例是准确的。