【问题标题】:Redux - How to handle multiple actions of the same type at the same timeRedux - 如何同时处理相同类型的多个操作
【发布时间】:2021-05-13 10:00:28
【问题描述】:

我有一个歌曲列表,每一行都有类似的功能。当我点击like按钮时,触发了LIKE_SONG动作,然后发出异步API请求,我们可以收到LIKE_SONG_SUCCESS动作或带有错误的LIKE_SONG_FAILURE动作。

为了处理不同的错误/成功/加载状态,我有一个 BaseStore,看起来像这样:

export type ActionType<T> = {
  type: string,
  payload?: T,
};

export type ActionStore = {
  actions: Array<ActionType<any>>,
  errorActions: Array<ActionType<any>>,
  successActions: Array<ActionType<any>>,
};

我指定启动/停止/错误操作创建者,它们将任何操作作为参数并将它们存储在ActionStore 中。所以在我所有的传奇中,我都有这样的东西:

  yield put(startAction(someAction));
  try {
    // do something
  } catch (error) {
    yield put(errorAction(someAction, error));
  } finally {
    yield put(stopAction(someAction));
  }

接下来,我创建了一些自定义选择器,用于选择正在加载/失败/成功的操作,例如

 export const checkLoadingSelector = (
  state: ActionStore,
  actionsToCheck: Array<string>
 ): boolean => {
  const { actions } = state;
  return actions.some((action) => {
    return actionsToCheck.includes(action.type);
   });
 };

我稍后会在这样的一些组件容器中使用它:

const mapStateToProps = ({ actionStore }) => {
  return {
    isSongLikeLoading: checkLoadingSelector(actionStore, [songActions.LIKE_SONG]),
  };
};

这在大多数情况下都可以正常工作。如果有多个同名操作,PROBLEM 就会启动。假设我快速点击 5 个不同的歌曲按钮并触发 5 个LIKE_SONG 动作。现在有2个问题:

  1. mapStateToProps 中的 isSongLikeLoading 将设置为 TRUE,直到这 5 个请求中的最后一个完成,这是不好的。

  2. 当第一个 LIKE_SONG 操作完成时,将触发 stopAction(someAction),这会从 ActionStore 操作数组中删除 LIKE_SONG 操作。问题再次是 LIKE_SONG 不是唯一的,当时我们有 5 个 LIKE_SONG 操作,因此我们可以删除错误的操作,这会产生错误的 UI 状态。

我不想要这个问题的有效代码解决方案,这不是我要寻找的。我想开始讨论如何正确设计我的商店/操作以轻松处理(和区分)同时触发的多个相同类型的操作,从而在我的应用程序中提供良好的用户体验。我读过的大多数文章都解决了非常简单的情况,即同时没有多个相同类型的动作,因此我不知道我是否做错了什么,这个问题在其他项目中不存在或者是什么案例

我目前解决此问题的想法是为这些操作添加一个唯一的 ID。

export type ActionType<T> = {
  id: string // unique id generated probably using uuid() method
  type: string,
  payload?: T,
};

然而,这种方法有两个问题:

  1. 不确定这个 id 字段是否是有效字段,通过 Redux 文档我看到操作应该只有 {type, payload, meta, error} 字段
  2. 检查checkLoadingSelector 将不再那么方便使用。用户而不是type 需要插入一个独特的操作id,首先他需要通过调用一些操作来获得它:
const mapDispatchToProps = (dispatch) => {
  return {
/* currently I return here void, in the solution I have in my mind I would return a string (the unique id of the action), not sure if this is possible however. Then in the component I would store the ongoing ids and base my loading state on those ids rather than on the action type (which is not unique) */
    likeSong: (songId): string => {
      dispatch(likeSongAction(songId));
    },
  };
};

这是一个好方法吗?你有什么想法吗?

【问题讨论】:

  • 您必须通过诸如likeSong(123) 和dislikeSong(123) 之类的异步操作创建者调用类似于:/songs/123/like (123: songId) 和/songs/123/dislike 的API。现在,您只需要在减速器中收到LIKE_SUCCESS(和类似的)操作时找出songId。 (这应该很容易使用 URL 或元数据,就像您已经找到的那样。)在减速器中找到 songId 之后,您可以为每个 songId 做不同的事情,因此,可以以不同的方式处理 ACTIONS。

标签: javascript reactjs typescript redux redux-saga


【解决方案1】:

只需使用batch:

import { batch } from 'react-redux'

function myThunk() {
  return (dispatch, getState) => {
    // should only result in one combined re-render, not two
    batch(() => {
      dispatch(increment())
      dispatch(increment())
    })
  }
}

【讨论】:

    猜你喜欢
    • 2013-07-11
    • 2021-04-09
    • 1970-01-01
    • 2017-06-04
    • 1970-01-01
    • 1970-01-01
    • 2021-09-28
    • 2018-11-02
    • 2015-07-26
    相关资源
    最近更新 更多