异步冗余.错误:修饰符可能不会发出操作



我一直遇到似乎是并行异步冗余错误的情况。在高级别上,我正在尝试调度一堆从 url 获取资产的异步操作。

我最初尝试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)
    }
  }

您提到您在 componentDidMount 中调度操作,但您的示例显示您实际上是从 componentDidUpdate 调度的。

class CharacterView extends Component {
  componentDidUpdate(){ // <--- should be componentDidMount?
    if(this.props.missingItemAssets){
      this.props.missingItemAssets.forEach((assetName) => {
        this.props.assetsFetchBinary(assetName);
      });
    }
  }
}

这可能会导致您的问题,假设您在问题中的示例是准确的。

相关内容

最新更新