async redux。错误:修饰符可能不会发出操作

时间:2017-10-27 01:44:54

标签: javascript reactjs redux

我一直遇到似乎是并行的异步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)
    }
  }

1 个答案:

答案 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);
      });
    }
  }
}

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