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

时间:2016-06-10 00:35:46

标签: javascript redux

我们有一个提取对象异步的动作,我们称之为getPostDetails,它接受​​一个参数来获取哪个帖子 一个身份证。向用户显示一个帖子列表,可以单击一个以获取一些详细信息。

如果用户点击“发布#1”,我们会发送GET_POST行为,可能看起来像这样。

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

这是由中间件拾取的,它为promise添加了一个成功处理程序,它将调用一个类似的动作 GET_POST__OK使用反序列化的JSON对象。 reducer看到这个对象并将其应用于商店。一个典型的 __OK reducer看起来像这样。

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

在该行的后面,我们有一个查看currentPost的组件,并显示当前帖子的详细信息。

但是,我们有竞争条件。如果用户一个接一个地提交两个GET_POST_DETAILS个动作,则有 如果第二个http请求在第一个http请求完成之前完成,则无法保证我们接收__OK个动作的顺序 国家将变得不正确。

    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

我们如何确保用户点击的最后一项始终优先?

2 个答案:

答案 0 :(得分:83)

问题是由于国家组织欠佳。

在Redux应用程序中,像currentPost这样的状态键通常是反模式。如果每次导航到另一个页面时都必须“重置”状态,则会丢失main benefits of Redux (or Flux):缓存中的一个。例如,如果任何导航重置状态并重新获取数据,则无法再立即导航回来。

存储此信息的更好方法是将postsByIdcurrentPostId分开:

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

现在,您可以根据需要同时获取任意数量的帖子,并将它们独立地合并到postsById缓存中,而不必担心所提取的帖子是否为当前帖子。

在组件内部,您始终会从reducer文件中读取state.postsById[state.currentPostId]或更好的导出getCurrentPost(state)选择器,以便组件不依赖于特定的状态形状。

现在没有竞争条件你有一个帖子缓存,所以当你回去时你不需要重新获取。稍后,如果您希望从URL栏控制当前帖子,则可以完全从Redux状态中删除currentPostId,而是从路由器中读取它 - 其余逻辑将保持不变。

虽然这并不完全相同,但我碰巧有另一个类似问题的例子。查看code beforecode after。与你的问题不一样相当,但希望它能说明国家组织如何帮助避免竞争条件和不一致的道具。

我还录制了一个免费视频系列,解释了这些主题,因此您可能需要check it out

答案 1 :(得分:4)

Dan的解决方案可能是更好的解决方案,但另一种解决方案是在第二个请求开始时中止第一个请求。

您可以通过将您的操作创建者分成异步来执行此操作,该异步操作可以从商店读取并发送其他操作,redux-thunk允许您执行此操作。

您的异步操作创建者应该做的第一件事是检查商店是否有现有的承诺,如果有的话,中止它。如果没有,它可以发出请求,然后发出“请求”开始' action,包含promise对象,下次存储。

这样,只有最近创建的承诺才能解决。如果有,您可以使用收到的数据发送成功操作。如果承诺因某种原因被拒绝,您也可以发送错误操作。