【发布时间】: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个问题:
-
mapStateToProps 中的
isSongLikeLoading将设置为TRUE,直到这 5 个请求中的最后一个完成,这是不好的。 -
当第一个
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,
};
然而,这种方法有两个问题:
- 不确定这个
id字段是否是有效字段,通过 Redux 文档我看到操作应该只有{type, payload, meta, error}字段 - 检查
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