4

在 redux-observable 中,我需要在执行 API 请求之前等待,直到另一个史诗完成。使用 combineLatest 不起作用,APP_INITIALIZED 完成后没有任何反应。我也试过 withLatestFrom 但都不起作用。

const apiGetRequestEpic = (action$, store) => {
  return Observable.combineLatest(
    action$.ofType('APP_INITIALIZED'),
    action$.ofType('API_GET_REQUEST')
      .mergeMap((action) => {
        let url = store.getState().apiDomain + action.payload;
        return ajax(get(url))
          .map(xhr => {
            return {type: types.API_RESPONSE, payload: xhr.response};
          })
          .catch(error => {
            return Observable.of({ type: 'API_ERROR', payload: error });
          });
      })
  );
};

结合最新定义

4

2 回答 2

8

一种方法是(使用伪名称),当接收到初始操作时,API_GET_REQUEST您会立即开始侦听单个INITIALIZE_FULFILLED,这表明初始化(或其他)已完成——我们实际上将在稍后启动它。收到后,我们mergeMap(或switchMap您的用例中的任何一个)将其加入我们的调用中,以发出其他 ajax 请求并执行常规业务。最后,启动我们正在等待的实际初始化的技巧是startWith()在整个链的末尾添加 a ——它将发出并调度另一个史诗正在等待的动作。

const initializeEpic = action$ =>
  action$.ofType('INITIALIZE')
    .switchMap(() =>
      someHowInitialize()
        .map(() => ({
          type: 'INITIALIZE_FULFILLED'
        }))
    );

const getRequestEpic = (action$, store) =>
  action$.ofType('API_GET_REQUEST')
    .switchMap(() =>
      action$.ofType('INITIALIZE_FULFILLED')
        .take(1) // don't listen forever! IMPORTANT!
        .switchMap(() => {
          let url = store.getState().apiDomain + action.payload;
          return ajax(get(url))
            .map(xhr => ({
              type: types.API_RESPONSE,
              payload: xhr.response
            }))
            .catch(error => Observable.of({
              type: 'API_ERROR',
              payload: error
            })); 
        })
        .startWith({
          type: 'INITIALIZE'
        })
    );

你没有提到一切是如何工作的,所以这只是你需要为你的用例修改的伪代码。


综上所述,如果您在此位置之外从不调用该初始化,您也可以直接将该代码包含在单个史诗本身中,或者只是创建一个对其进行抽象的辅助函数。将它们作为单独的史诗通常意味着您的 UI 代码可以独立地触发它们中的任何一个——但为了测试目的将它们分开可能仍然很好。只有你可以打那个电话。

const getRequestEpic = (action$, store) =>
  action$.ofType('API_GET_REQUEST')
    .switchMap(() =>
      someHowInitialize()
        .mergeMap(() => {
          let url = store.getState().apiDomain + action.payload;
          return ajax(get(url))
            .map(xhr => ({
              type: types.API_RESPONSE,
              payload: xhr.response
            }))
            .catch(error => Observable.of({
              type: 'API_ERROR',
              payload: error
            }));
        })
        .startWith({ // dunno if you still need this in your reducers?
          type: 'INITIALIZE_FULFILLED'
        })
    );
于 2017-02-23T22:19:32.023 回答
2

我认为您没有combineLatest按照预期的方式使用。你的例子应该写成:

const apiGetRequestEpic = (action$, store) => {
  return (
    Observable.combineLatest(
      action$.ofType('APP_INITIALIZED').take(1),
      action$.ofType('API_GET_REQUEST')
    )
    .map(([_, apiGetRequest]) => apiGetRequest)
    .mergeMap((action) => {
      let url = store.getState().apiDomain + action.payload;
      return ajax(get(url))
        .map(xhr => {
          return {type: types.API_RESPONSE, payload: xhr.response};
        })
        .catch(error => {
          return Observable.of({ type: 'API_ERROR', payload: error });
        });
    })
  );
};

这样,只要发出 an ,这个 combineLatest 就会'API_GET_REQUEST'发出,前提是'APP_INITIALIZED'它至少被分派过一次,或者,如果没有,则等待它被分派。

注意.take(1). 没有它,你的组合 observable 也会在任何时候'APP_INITIALIZED'发出,很可能不是你想要的。

于 2019-02-07T12:52:25.707 回答