RXJS:重试时的调度操作

RXJS: Dispatch action in retryWhen

我编写了一个 React、redux、rxjs 应用程序,它每 5 秒从 API 中获取一次数据。每次获取应用程序时,我都会显示一个加载指示器。

如果调用失败,它将以 retryWhen 机制启动,然后我需要将商店从 loading 更新为 retrying,以便我可以更新视图。

目前我卡住了,因为我不知道如何从 retryWhen.

更新商店

应用组件

import React, { Component } from 'react';
import PropTypes from 'prop-types';

class App extends Component {
  state = {};

  componentWillMount() {
    this.fetchAdvertisement();
  }

  fetchAdvertisement = () => {
    console.log('Start advertisement call');
    console.log('---------------------------------------------');
    const { displayId, unitId } = this.props.match.params;
    this.props.fetchAdvertisement(displayId, unitId);
  };

  render() {
    return <div>Hello world</div>;
  }
}

App.propTypes = {
  fetchAdvertisement: PropTypes.func.isRequired,
  match: PropTypes.shape({
    params: PropTypes.shape({
      displayId: PropTypes.string.isRequired,
      unitId: PropTypes.string.isRequired,
    }).isRequired,
  }).isRequired,
};

export default App;

动作创作者

import * as ACTION_TYPES from 'modules/contants';

export function fetchAdvertisement(displayId, unitId) {
  return { type: ACTION_TYPES.ADVERTISEMENT_FETCH, payload: { displayId, unitId } };
}

export function fetchAdvertisementSuccess(payload) {
  return { type: ACTION_TYPES.ADVERTISEMENT_FETCH_SUCCESS, payload };
}

export function fetchAdvertisementRetrying() {
  return { type: ACTION_TYPES.ADVERTISEMENT_FETCH_RETRYING };
}

export function fetchAdvertisementFailed(payload) {
  return { type: ACTION_TYPES.ADVERTISEMENT_FETCH_FAILED, payload };
}

Epics

export const retryMechanism = ({
    scalingDuration = 1000,
    excludedStatusCodes = []
  } = {}) => attempts =>
    attempts.pipe(
      mergeMap((error, i) => {
      // Update the store by dispatching the action creator fetchAdvertisementRetrying

      const retryAttempt = i + 1;
      if (excludedStatusCodes.find(e => e === error.status)) {
        return _throw(error);
      }
      console.log(`Attempt ${retryAttempt}: retrying in ${scalingDuration * retryAttempt}s`);

      return timer(scalingDuration * retryAttempt);
    })
  );

export const fetchAdEpic = action$ =>
  action$.ofType(ACTION_TYPES.ADVERTISEMENT_FETCH).mergeMap(action =>
    ajax
      .getJSON(`${process.env.REACT_APP_API_URL}/request-ad/${action.payload.displayId}`)
      .map(response => {
        if (response.data) {
          return fetchAdvertisementSuccess(response.data);
        }
      })
      .retryWhen(retryMechanism())
      .catch(error => {
        console.log(error);
        return Observable.of(fetchAdvertisementFailed(error.data));
      })
  );

减速器

import * as ACTION_TYPES from 'modules/contants';

const initialState = {};

export default (state = initialState, action) => {
  switch (action.type) {
    case ACTION_TYPES.ADVERTISEMENT_FETCH: {
      console.log('Loading');
      console.log('---------------------------------------------');
      return state;
    }

    case ACTION_TYPES.ADVERTISEMENT_FETCH_RETRYING: {
      console.log('Retrying');
      console.log('---------------------------------------------');
      return state;
    }

    case ACTION_TYPES.ADVERTISEMENT_FETCH_SUCCESS: {
      console.log('success');
      console.log('---------------------------------------------');
      return state;
    }

    case ACTION_TYPES.ADVERTISEMENT_FETCH_FAILED: {
      console.log('error');
      console.log('---------------------------------------------');
      return state;
    }

    default: {
      return state;
    }
  }
};

我不知道 rxjs 是如何工作的,所以很多代码对我来说都很陌生。快速浏览 rxjs middleware Epic documentation here 表明中间件将两个参数传递给您的史诗:action$store。因此,您可以按如下方式重写代码:

export const retryMechanism = (dispatch) => ({ scalingDuration = 1000, excludedStatusCodes = [] } = {}) => attempts =>
    attempts.pipe(mergeMap((error, i) => {
        // Let the store know i am retry-ing by calling the function fetchAdvertisementRetrying
        dispatch(fetchAdvertisementRetrying());
        const retryAttempt = i + 1;
        if (excludedStatusCodes.find(e => e === error.status)) {
            return _throw(error);
        }
        console.log(`Attempt ${retryAttempt}: retrying in ${scalingDuration * retryAttempt}s`);
        return timer(scalingDuration * retryAttempt);
    })
);

export const fetchAdEpic = (action$, store) =>
    action$.ofType(ACTION_TYPES.ADVERTISEMENT_FETCH).mergeMap(action =>
        ajax
        .getJSON(`${process.env.REACT_APP_API_URL}/request-ad/${action.payload.displayId}`)
        .map(response => {
            if (!response.data) {
                return fetchAdvertisementSuccess(response.data);
            }
        })
        .retryWhen(retryMechanism(store.dispatch)())
        .catch(error => {
            console.log(error);
            return Observable.of(fetchAdvertisementFailed(error.data));
        })
);

注意:(同样来自文档)提供给您的史诗的商店是商店的 light-weight 版本,而不是完整的商店 object。提供的商店仅包含方法 getStatedispatch.