为无关的reducer

时间:2016-11-10 14:02:00

标签: reactjs redux

我有以下用于调用类似异步调用的中间件:

import { callApi } from '../utils/Api';

import generateUUID from '../utils/UUID';

import { assign } from 'lodash';

export const CALL_API = Symbol('Call API');

export default store => next => action => {
  const callAsync = action[CALL_API];

  if(typeof callAsync === 'undefined') {
    return next(action);
  }

  const { endpoint, types, data, authentication, method, authenticated } = callAsync;

  if (!types.REQUEST || !types.SUCCESS || !types.FAILURE) {
    throw new Error('types must be an object with REQUEST, SUCCESS and FAILURE');
  }

  function actionWith(data) {
    const finalAction = assign({}, action, data);
    delete finalAction[CALL_API];

    return finalAction;
  }

  next(actionWith({ type: types.REQUEST }));

  return callApi(endpoint, method, data, authenticated).then(response => {
    return next(actionWith({
      type: types.SUCCESS,
      payload: {
        response
      }
    }))
  }).catch(error => {
    return next(actionWith({
      type: types.FAILURE,
      error: true,
      payload: {
        error: error,
        id: generateUUID()
      }
    }))
  });
};

然后我在组件componentWillMount中进行以下调用:

  componentWillMount() {
    this.props.fetchResults();
    this.props.fetchTeams();
  }
例如,

fetchTeams将调度由中间件处理的操作,如下所示:

export function fetchTeams() {
  return (dispatch, getState) => {
    return dispatch({
      type: 'CALL_API',
      [CALL_API]: {
        types: TEAMS,
        endpoint: '/admin/teams',
        method: 'GET',
        authenticated: true
      }
    });
  };
}

调度成功操作并从reducer返回新状态。两个Reducer看起来都一样,下面是Teams reducer:

export const initialState = Map({
  isFetching: false,
  teams: List()
});

export default createReducer(initialState, {
  [ActionTypes.TEAMS.REQUEST]: (state, action) => {
    return state.merge({isFetching: true});
  },

  [ActionTypes.TEAMS.SUCCESS]: (state, action) => {
    return state.merge({
      isFetching: false,
      teams: action.payload.response
    });
  },

  [ActionTypes.TEAMS.FAILURE]: (state, action) => {
    return state.merge({isFetching: false});
  }
});

然后该组件呈现另一个分派另一个动作的组件:

render() {
  <div>
   <Autocomplete items={teams}/>
  </div>
}

自动填充然后在componentWillMount

中调度操作
class Autocomplete extends Component{
  componentWillMount() {
    this.props.dispatch(actions.init({ props: this.exportProps() }));
  }

在从父组件fetchTeams的原始调用中调用fetchResultscomponentWillUpdate的SUCCESS减少器后调用的自动完成减少器中发生错误但由于某种原因调用第一个代码片段中的中间件中的catch处理程序:

  return callApi(endpoint, method, data, authenticated).then(response => {
    return next(actionWith({
      type: types.SUCCESS,
      payload: {
        response
      }
    }))
  }).catch(error => {
    return next(actionWith({
      type: types.FAILURE,
      error: true,
      payload: {
        error: error,
        id: generateUUID()
      }
    }))
  });
};

我不明白为什么调用catch处理程序,因为我认为此处的promise已经解决了。

1 个答案:

答案 0 :(得分:1)

我不完全确定,通过阅读代码很难调试。显而易见的答案是因为它发生在next(actionWith({ type: types.SUCCESS, payload: { response } }))调用的同一堆栈跟踪中。

所以在这种情况下:

  1. 中间件:在fetchTeam
  2. 内发送Promise.then次成功
  3. Redux更新道具
  4. React:渲染新道具
  5. React:componentWillMount
  6. React:发布新动作
  7. 如果在任何时候发生错误,它会冒泡到Promise.then,然后使其执行Promise.catch回调。

    尝试在setTimeout内调用自动完成提取,让当前的堆栈跟踪完成并在下一个“事件循环”中运行提取。

    setTimeout(
      () => this.props.dispatch(actions.init({ props: this.exportProps() }))
    );
    

    如果这样做,那么'事件循环在错误发生时没有完成处理,从中间件成功调度一直到自动完成呈现的事实是函数调用后的函数调用。

    注意:您应该考虑将redux-loop或redux-saga用于异步任务,如果您想继续使用自定义中间件,也许您可​​以从库中获取有关如何制作的灵感你的api请求来自初始调度的异步。