如何在使用Redux获取数据时避免争用条件?

ql3eal8s  于 2022-12-13  发布在  其他
关注(0)|答案(2)|浏览(128)

我们有一个异步获取对象的操作,我们称之为getPostDetails,它通过一个id获取一个参数,该参数表示要获取哪个帖子。用户将看到一个帖子列表,可以单击其中一个帖子来获得一些详细信息。
如果用户点击“Post #1”,我们将发送一个GET_POST操作,如下所示。

const getPostDetails = (id) => ({
  type: c.GET_POST_DETAILS,
  promise: (http) => http.get(`http://example.com/posts/#${id}`),
  returnKey: 'facebookData'
})

中间件会接收到这个消息,并向promise添加一个成功处理程序,它将使用反序列化的JSON对象调用GET_POST__OK这样的操作。reducer看到这个对象并将其应用到一个存储中。典型的__OK reducer如下所示。

[c.GET_ALL__OK]: (state, response) => assign(state, {
  currentPost: response.postDetails
})

接下来,我们有一个组件,它查看currentPost并显示当前帖子的详细信息。
但是,我们有一个竞争条件。如果用户一个接一个地提交两个GET_POST_DETAILS操作,我们不能保证收到__OK操作的顺序,如果第二个http请求在第一个之前完成,状态将变得不正确。

Action                   => Result
    ---------------------------------------------------------------------------------
|T| User Clicks Post #1      => GET_POST for #1 dispatched => Http Request #1 pending
|i| User Clicks Post #2      => GET_POST for #2 dispatched => Http Request #2 pending
|m| Http Request #2 Resolves => Results for #2 added to state
|e| Http Request #1 Resolves => Results for #1 added to state
 V

我们如何确保用户单击的最后一项始终具有优先级?

gmol1639

gmol16391#

问题是由于次优的状态组织。
在Redux应用中,状态键(如currentPost)通常是反模式。如果每次导航到另一个页面时都必须“重置”状态,则会丢失main benefits of Redux (or Flux)缓存。例如,如果任何导航重置了状态并重新获取了数据,则您将无法再立即向后导航。
存储此信息的更好方法是将postsByIdcurrentPostId分开:

{
  currentPostId: 1,
  postsById: {
    1: { ... },
    2: { ... },
    3: { ... }
  }
}

现在,您可以同时获取任意多个post,并将它们独立地合并到postsById缓存中,而不必担心获取的post是否是当前的post。
在组件内部,您将始终读取state.postsById[state.currentPostId],或者更好的方法是从reducer文件中导出getCurrentPost(state)选择器,以便组件不依赖于特定的状态形状。
现在没有竞争条件 * 并且 * 您有一个帖子缓存,因此当您返回时不需要重新获取。稍后,如果您希望当前帖子由URL栏控制,您可以将currentPostId从Redux状态中完全删除,而代之以从您的路由器读取它-其余的逻辑将保持不变。
虽然这并不完全相同,但我碰巧有另一个例子也有类似的问题。看看code beforecode after。它和你的问题并不 * 完全 * 相同,但希望它能展示状态组织如何帮助避免争用条件和不一致的 prop 。
我还录制了一个免费的视频系列,解释这些主题,所以你可能想要check it out

3qpi33ja

3qpi33ja2#

Dan的解决方案可能更好,但另一种解决方案是在第二个请求开始时中止第一个请求。
您可以通过将操作创建器拆分为一个异步创建器来实现这一点,该异步创建器可以从存储中读取并调度其他操作,这是redux-thunk允许您实现的。
异步操作创建者应该做的第一件事是检查存储中是否存在现有的承诺,如果存在,则中止它。如果没有,则它可以发出请求,并调度一个“请求开始”操作,该操作包含承诺对象,该对象将被存储以备下次使用。
这样,只有最近创建的承诺才会得到解决。当一个承诺得到解决时,您可以使用接收到的数据调度成功活动。如果承诺由于某种原因被拒绝,您也可以调度错误活动。

相关问题