在可观察到的redux中,我如何在执行任何其他操作之前触发一个操作

用户名

背景:

我正在使用史诗来管理请求。

对于每个请求,我发送一个令牌,该令牌可能会过期,但是可以在宽限期内刷新。

我正在为每个请求使用令牌,但是在发送任何请求之前,我想检查令牌是否已过期,以及是否已过期且具有宽限期,然后首先刷新令牌,然后继续执行相应的操作

所有的请求都有自己的史诗。

现在,我尝试的是对所有操作进行预检查,以检查令牌是否可能刷新它,然后继续进行操作。

希望这能解释。

// epics for querying data
// these epics are using a token, that is stored in redux state.

const getMenuEpic = action$ => ....

const getFoodListEpic = action$ => ....

const getFoodItemEpic = action$ => ....

...


// helper function to check 
// if token has expired

const hasTokenExpired = (token) => .....

// refresh token 
// this returns a new token in the promise

const refreshToken = fetch('http://.../refresh-toekn')

// i am trying to make an epic, that will fire 
// before any actions in the application
// stop the action (let's call it action A)
// get token from redux state, 
// verify if is valid or not
// if invalid call refresh token (async process), 
// and when refresh token finished, proceed with the incoming action A
// if the token was valid then continue with action A.

const refreshEpic = (action$, store) => 
 action$.map(() => store.getState().token)
  .filter(Boolean)
  .filter(token => hasTokenExpired(token))
  .mergeMap(() => refreshToken()) ...

 ......

但是这种方法不适用于refreshEpic

Jayphelps

不可能真正阻止某个动作到达还原器-实际上在传递给您的史诗之前就已经通过了-而是可以分派一个动作来表示要获取的意图,但实际上并不是触发它的原因。例如,UI调度FETCH_SOMETHING,而史诗般地看到它,确认有一个有效的刷新令牌(或获取一个新的刷新令牌),然后发出另一个操作以实际触发获取,例如FETCH_SOMETHING_WITH_TOKEN。

在这种情况下,尽管您可能会遇到许多具有相同要求的史诗,但这样做可能会很乏味。有很多方法可以使此操作变得容易。这是一对:

将提取的内容包装在帮助程序中

您可以编写一个帮您检查的助手,如果需要刷新,它将请求并等待,然后再继续。我将亲自在单独的专用史诗中处理实际的刷新,以防止出现多个并发刷新请求以及诸如此类的事情。

const requireValidToken = (action$, store, callback) => {
  const needsRefresh = hasTokenExpired(store.getState().token);

  if (needsRefresh) {
    return action$.ofType(REFRESH_TOKEN_SUCCESS)
      .take(1)
      .takeUntil(action$.ofType(REFRESH_TOKEN_FAILED))
      .mergeMap(() => callback(store.getState().token))
      .startWith({ type: REFRESH_TOKEN });
  } else {
    return callback(store.getState().token);
  }
};

const getMenuEpic = (action$, store) =>
  action$.ofType(GET_MENU)
    .switchMap(() =>
      requireValidToken(action$, store, token =>
        actuallyGetMenu(token)
          .map(response => getMenuSuccess(response))
      )
    );

“超级史诗”

编辑:这是我最初的建议,但比上面的建议要复杂得多。它也有一些好处,但是IMO上面的一项将更易于使用和维护。

相反,您可以创建一个“超级史诗”,一个史诗本身由其他史诗组成并委托给其他史诗。根史诗是超史诗的一个例子。(我现在刚刚编造了这个词...大声笑)

我们可能要做的一件事是区分任何随机动作和需要auth令牌的动作-您不想检查auth令牌并为曾经调度的每个动作刷新它。一种简单的方法是在操作中包含一些元数据,例如{ meta: { requiresAuth: true } }

这要复杂得多,但是在其他解决方案上也有优势。这是我在说的一个粗略想法,但是它未经测试,可能不是100%考虑的。考虑它的灵感,而不是复制面食。

// action creator helper to add the requiresAuth metadata
const requiresAuth = action => ({
  ...action,
  meta: {
    ...action.meta,
    requiresAuth: true
  }
});
// action creators
const getMenu = id => requiresAuth({
  type: 'GET_MENU',
  id
});
const getFoodList = () => requiresAuth({
  type: 'GET_FOOD_LIST'
});

// epics
const getMenuEpic = action$ => stuff
const getFoodListEpic = action$ => stuff

const refreshTokenEpic = action$ =>
  action$.ofType(REFRESH_TOKEN)
    // If there's already a pending refreshToken() we'll ignore the new
    // request to do it again since its redundant. If you instead want to
    // cancel the pending one and start again, use switchMap()
    .exhaustMap(() =>
      Observable.from(refreshToken())
        .map(response => ({
          type: REFRESH_TOKEN_SUCCESS,
          token: response.token
        }))
        // probably should force user to re-login or whatevs
        .catch(error => Observable.of({
          type: REFRESH_TOKEN_FAILED,
          error
        }))
    );


// factory to create a "super-epic" which will only
// pass along requiresAuth actions when we have a
// valid token, refreshing it if needed before.
const createRequiresTokenEpic = (...epics) => (action$, ...rest) => {
  // The epics we're delegating for
  const delegatorEpic = combineEpics(...epics);
  // We need some way to emit REFRESH_TOKEN actions
  // so I just hacked it with a Subject. There is
  // prolly a more elegant way to do this but #YOLO
  const output$ = new Subject();

  // This becomes action$ for all the epics we're delegating
  // for. This will hold off on giving an action to those
  // epics until we have a valid token. But remember,
  // this doesn't delay your *reducers* from seeing it
  // as its already been through them!
  const filteredAction$ = action$
    .mergeMap(action => {
      if (action.meta && action.meta.requiresAuth) {
        const needsRefresh = hasTokenExpired(store.getState().token);

        if (needsRefresh) {
          // Kick off the refreshing of the token
          output$.next({ type: REFRESH_TOKEN });

          // Wait for a successful refresh
          return action$.ofType(REFRESH_TOKEN_SUCCESS)
            .take(1)
            .mapTo(action)
            .takeUntil(action$.ofType(REFRESH_TOKEN_FAILED));
            // Its wise to handle the case when refreshing fails.
            // This example just gives up and never sends the
            // original action through because presumably
            // this is a fatal app state and should be handled
            // in refreshTokenEpic (.e.g. force relogin)
        }
      }

      // Actions which don't require auth are passed through as-is
      return Observable.of(action);
    });

  return Observable.merge(
    delegatorEpic(filteredAction$, ...rest),
    output$
  );
};

const requiresTokenEpic = createRequiresTokenEpic(getMenuEpic, getFoodList, ...etc);

如前所述,有许多方法可以解决此问题。我可以设想创建一些需要令牌的史诗内部使用的辅助函数,而不是这种“超级史诗”方法。做对您来说似乎不太复杂的事情。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

可观察到的Redux:如何从回调返回操作?

在Redux可观察到的史诗中的所有动作之后触发一个动作

jQuery-如何确保用户在执行任何其他操作之前输入文本?

在 JQuery 中执行任何其他操作之前等待事件发生

Selenium:如何在加载/执行页面的任何其他脚本之前将Javascript插入/执行到页面中?

httpClient的http操作是否总是单一值可观察到的?他们能以某种方式发出多于一个的价值吗?

如何在RxJS中创建一个可观察对象,它依次等待2个可观察对象,然后执行操作,然后依次等待这2个可观察对象

对流redux的重复操作可观察到

在Jest中,如何对一个可观察到的方法进行单元测试

RxJs。如果它最近被触发,如何等待其他可观察到的东西

可观察到的Redux:操作完成后组件如何订阅以做出反应?

创建新的类实例时,如何在 Python 中的任何其他类方法之前自动运行类的一个方法

将一个可观察到的RxJS结果传递到下一个可观察到的序列中

RxJava可观察到的最后一个状态

SwiftUI观察到变量后如何立即触发操作

如何在执行任何其他命令之前执行“清除” bash命令?

从可观察到的淘汰赛中删除最后一个角色

可观察到的Redux:在多次点击(两次或更多次)上分派操作

在做任何其他事情之前等待一个方法任务完成

组合多个可观察对象并在Angular中返回至少一个值时执行操作

如何制作一个可以执行传递给它的任何其他方法的方法

如何在一个查询中执行此操作?

如何在可观看Redux的史诗中链接可观察到的RxJS?

链式可观察到的Redux史诗只能正确触发一次

如何在vue.js中执行其他操作之前等待函数的响应

RXJS Redux可观察到执行多个API调用

删除可观察到的操作,因为订阅已取消

仅按一个键分组,不执行任何其他更改

我想将ID添加到div中,与敲除中可观察到的数组的第一个值相同