Redux:我如何让两个Reducer以特定顺序响应同一个动作?

时间:2016-08-19 19:10:48

标签: redux

我对Redux比较新,我有以下情况:

  1. 我的应用程序发出一个AJAX请求来获取第一条"消息"来自服务器的对象
  2. 响应回来并发送{type: 'RECEIVE_FIRST_MESSAGE', message}
  3. 我的MessageReducer处理RECEIVE_FIRST_MESSAGE

    A)将action.message添加到state.messages

    B)进行另一次AJAX调用以获取下一条消息(如果action.message.hasNextMessage

  4. 我的SessionReducer也处理RECEIVE_FIRST_MESSAGE,将action.message.sessionId添加到state.session.id

  5. 只有一个问题:当我获取第二条消息时,我需要使用我从第一条消息中获得的sessionId。但由于MessageReducerSessionReducer之前运行,因此会话ID不在MessageReducer尝试发出第二个AJAX请求时的状态。

    我可以解决"这可以通过使用超时来确保MessageReducer在1毫秒之后不会发出第二个请求,但这感觉就像是黑客/反模式。

    TLDR

    当我有:

    • 包含state两部分数据的动作(由两个不同的减速器处理)
    • 我想从reducer A
    • 中触发响应的AJAX调用
    • 我想从reducer B
    • 添加一些状态(state.session.id)作为响应

    如何确保减速器B在减速器A进行AJAX调用之前添加状态(以不会让Dan Abramov哭泣的方式)?

1 个答案:

答案 0 :(得分:5)

首先要做的事情。切勿从减速机内部进行AJAX调用。永远。 https://www.youtube.com/watch?v=Q-lv8kyYrqQ

Reducer函数应该是纯粹的,没有任何副作用(如HTTP请求)。相反,使用thunks对你有利。以下是如何执行此操作的示例。

从服务器收到第一条消息响应后,您将调度RECEIVE_FIRST_MESSAGE操作。这将同步更新state atom。完成后,您可以检查消息是否有下一条消息。如果是,则调度新的异步操作以获取下一条消息。在异步操作创建器中,您可以从state atom获取会话ID,该会话ID保证在响应您之前发送的RECEIVE_FIRST_MESSAGE操作时进行更新。

如果您有任何问题,请与我们联系。

// getFirstMessageFromServer() and getNextMessageFromServer() are thunks and async action creators
// fetchFirstMessage() and fetchNextMessage() make HTTP requests

function getFirstMessageFromServer() {
  return (dispatch, getState) => {
    return fetchFirstMessage()
      .then(message => {
        dispatch({ type: 'RECEIVE_FIRST_MESSAGE', message });
        if (message.hasNextMessage) dispatch(getNextMessageFromServer());
        return message;
      });
  };
}

function getNextMessageFromServer() {
  return (dispatch, getState) => {
    const { id: sessionId } = getState().session.id;
    return fetchNextMessage(sessionId)
      .then(nextMessage => {
        dispatch({ type: 'RECEIVE_NEXT_MESSAGE', message: nextMessage });
        return nextMessage;
      });
  };
}


const rootReducer = combineReducers({
  session: sessionReducer,
  messages: messagesReducer
});

const initialMessages = [];

function messagesReducer(state = initialMessages, action) {
  switch(action.type) {
    case 'RECEIVE_FIRST_MESSAGE':
      return state.concat(action.message);
    case 'RECEIVE_NEXT_MESSAGE':
      return state.concat(action.message);
    default:
      return state;
  }
}

const initialSession = { id: null };

function sessionReducer(state = initialSession, action) {
  switch(action.type) {
    case 'RECEIVE_FIRST_MESSAGE':
      return {
        ...state,
        id: action.message.sessionId
      };
    default:
      return state;
  }
}