【问题标题】:Redux-Observable: modify state and trigger follow up actionRedux-Observable:修改状态并触发后续动作
【发布时间】:2019-08-29 08:26:10
【问题描述】:

我在 redux-observable 中有以下场景。我有一个组件可以检测要使用的后端,并且应该设置 api-client 使用的后端 URL。客户端和 URL 都保存在全局状态对象中。

执行顺序应该是: 1.检查后端 2. 错误替换处于状态的后端 URL 3. 触发 3 个动作以使用新的后端状态 URL 加载资源

到目前为止,我所做的是,在第 1 步中。从我的史诗中访问 state$ 对象并修改支持的 URL。这似乎只成功了一半。状态由 3 中触发的操作更新。仍然看到旧状态并使用错误的后端。

如果您依赖执行顺序,那么在操作之间更新状态的标准方法是什么?

我的 API-Epic 如下所示:

export const authenticate = (action$, state$) => action$.pipe(
    ofType(actions.API_AUTHENTICATE),
    mergeMap(action =>
        from(state$.value.apiState.apiClient.authenticate(state$.value.apiState.bearer)).pipe(
            map(bearer => apiActions.authenticatedSuccess(bearer))
        )
    )
)

export const authenticatedSuccess = (action$, state$) => action$.pipe(
   ofType(actions.API_AUTHENTICATED_SUCCESS),
    concatMap(action => concat(
        of(resourceActions.doLoadAResource()),
        of(resourceActions.doLoadOtherResource()),
        of(resourceActions.doLoadSomethingElse()))
      )
)

【问题讨论】:

  • 首先,您的apiClient 上的authenticate 函数有什么作用,它返回什么?其次,不清楚后端 URL 何时更新。响应您的 apiActions.authenticatedSuccess 操作的减速器中是否会发生这种情况?最后,resourceActions.* 动作有什么作用?他们是否开启了其他史诗?

标签: javascript reactjs react-redux rxjs redux-observable


【解决方案1】:

我发现用户在 GitHub 和 StackOverflow 上讨论的一种常见方法是链接多个史诗,就像我相信你的示例试图展示的那样。第一个史诗在“完成”时调度一个动作。 reducer 监听这个动作并更新 store 的状态。第二个史诗(或者如果您想要并发操作,则可以添加许多其他史诗)侦听相同的操作并启动工作流的下一个序列。次级史诗在减速器之后运行,因此可以看到更新的状态。 From the docs:

Epic 在正常的 Redux 调度通道旁边运行,在 reducer 已经收到它们之后...

我发现链接方法可以很好地解耦更大工作流程的各个阶段。出于设计原因(例如关注点分离),您可能希望解耦,以重用较大工作流的较小部分,或制作较小的单元以便于测试。当您的史诗在较大工作流的不同阶段之间分派操作时,这是一种简单的实施方法。

但是,请记住 state$ 是可观察的。您可以使用它在任何时间点获取 current 值——包括在单个史诗中调度不同操作之间。例如,考虑以下情况并假设我们的商店有一个简单的柜台:

export const workflow = (action$, state$) => action$.pipe(
  ofType(constants.START),
  withLatestFrom(state$),
  mergeMap(([action, state]) => // "state" is the value when the START action was dispatched
    concat(
      of(actions.increment()),
      state$.pipe(
        first(),
        map(state => // this new "state" is the _incremented_ value!
          actions.decrement()),
      ),
      defer(() => {
        const state = state$.value // this new "state" is now the _decremented_ value!
        return empty()
      }),
    ),
  ),
)

有很多方法可以从 observable 中获取当前状态!


关于您示例中的以下代码行:

state$.value.apiState.apiClient.authenticate(state$.value.apiState.bearer)

首先,使用状态传递 API 客户端并不是一种常见/推荐的模式。您可能想查看injecting the API client as a dependency 到您的史诗(这使得单元测试更容易!)。其次,不清楚 API 客户端如何从状态中获取 当前 后端 URL。 API客户端是否可能正在使用状态的缓存版本?如果是,您可能需要重构 authenticate 方法并传入当前后端 URL。

这是一个处理错误并结合上述内容的示例:

/**
 * Let's assume the state looks like the following:
 * state: {
 *   apiState: {
 *     backend: "URL",
 *     bearer: "token"
 * }
 */

// Note how the API client is injected as a dependency
export const authenticate = (action$, state$, { apiClient }) => action$.pipe(
  ofType(actions.API_AUTHENTICATE),
  withLatestFrom(state$),
  mergeMap(([action, state]) =>
    // Try to authenticate against the current backend URL
    from(apiClient.authenticate(state.apiState.backend, state.apiState.bearer)).pipe(
      // On success, dispatch an action to kick off the chained epic(s)
      map(bearer => apiActions.authenticatedSuccess(bearer)),
      // On failure, dispatch two actions:
      //   1) an action that replaces the backend URL in the state
      //   2) an action that restarts _this_ epic using the new/replaced backend URL
      catchError(error$ => of(apiActions.authenticatedFailed(), apiActions.authenticate()),
    ),
  ),
)

export const authenticatedSuccess = (action$, state$) => action$.pipe(
  ofType(actions.API_AUTHENTICATED_SUCCESS),
  ...
)

此外,请记住,在链接像 concat 这样的构造的史诗时,不会等待链接的史诗“完成”。例如:

concat(
  of(resourceActions.doLoadAResource()),
  of(resourceActions.doLoadOtherResource()),
  of(resourceActions.doLoadSomethingElse()))
)

如果这些doLoadXXX 动作中的每一个都“开始”了一段史诗,那么这三个动作很可能会同时运行。每个动作会一个接一个地被调度,每个史诗会一个接一个地“开始”运行,而不需要等待前一个“完成”。这是因为史诗从未真正完整。它们是长寿的,永无止境的溪流。如果您想在doLoadOtherResource doLoadAResource 之后运行,您将需要明确地等待一些标识doLoadAResource 何时完成的信号。

【讨论】:

    猜你喜欢
    • 2022-08-16
    • 2018-10-04
    • 2017-09-13
    • 2021-10-25
    • 2017-10-05
    • 1970-01-01
    • 2018-08-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多