如何为具有列表/详细信息视图和分页的应用选择Redux状态形状?

时间:2015-11-26 13:31:37

标签: javascript redux ngrx

想象一下,我的数据库中有很多条目(例如用户)。我还有两个路由,一个用于列表,另一个用于详细信息(您可以在其中编辑条目)。现在我正在努力解决如何处理数据结构问题。

我正在考虑两种方法和两种方式的组合。

共享数据集

  • 我导航到/list,我的所有用户都从存储在redux商店的api下载,在密钥users下,我还添加了某种users_offset和{{1仅呈现列表的一部分
  • 然后我导航到users_limit,并将/detail/<id>currently_selected_user一起存储为val ...这意味着我将能够以这样的方式获取用户的数据{{1 }}
  • 更新也很简单,因为我只使用一个数据集和指向它的细节
  • 添加新用户也很简单,再次使用相同的用户列表

现在我用这种方法遇到的问题是,当用户列表变得庞大(比如数百万)时,下载可能需要一段时间。而且,当我直接导航到<id>时,我还没有下载所有用户,所以为了获得我需要的数据,我将不得不首先下载整个内容。数以百万计的用户只需编辑一个。

分隔数据集

  • 我导航到users.find(res => res.id === currently_selected_user),而不是从api下载我的所有用户,而是根据我/detail/<id>/list的设置,我只下载了几个用户,我可能会将数据存储为users_per_page
  • 然后我导航到users_current_page,将users_currently_visible存储为/detail/<id>作为val ...而不是搜索currently_selected_user我只是从api下载用户的数据。
  • 关于更新,我不会以任何方式更新<id>
  • 我也不会添加

我认为这里可能存在的问题是,在访问users_currently_visible时,我将不得不再次从api下载数据,因为它可能与数据库中的内容不同步,我也可能是不必要地详细下载用户数据,因为它们可能偶然已经在我的users_currently_visible

某种frankenstein-y shenanigans

  • 我详细说明,我和分离数据集一样,但不是直接从api下载用户数据,我先检查:
    • 我有/list
    • 吗?
    • 如果是这样,我们之间是否有一个用户身份的用户? 如果两者都是真的,那么我将其用作我的用户数据,否则我进行api调用
  • 同样在更新时发生,我检查我的用户是否存在于users_currently_visible之间,如果是,我也会更新该列表,如果不是我什么都不做

这可能会奏效,但并不觉得这是正确的方法。在访问users_currently_visible时,我可能仍需要下载users_currently_visible的新列表,因为我可能添加了一个新列表..

有没有粉丝最喜欢这样做的方式?...我确信每一个redux用户都必须遇到同样的事情。

谢谢!

2 个答案:

答案 0 :(得分:75)

请咨询Redux repo的“real world” example 它显示了解决这个问题的方法。

你的状态应如下所示:

{
  entities: {
    users: {
      1: { id: 1, name: 'Dan' },
      42: { id: 42, name: 'Mary' }
    }
  },
  visibleUsers: {
    ids: [1, 42],
    isFetching: false,
    offset: 0
  }
}

注意我将分别存储entities(ID - &gt;对象地图)和visibleUsers(当前可见用户的分页状态和ID说明)。

这与您的“共享数据集”方法类似。但是我不认为你列出的缺点是这种方法固有的真正问题。我们来看看它们。

  

现在我用这种方法遇到的问题是,当用户列表变得庞大(比如数百万)时,下载可能需要一段时间

您无需下载所有内容!将所有下载的实体合并到entities并不意味着您应该查询所有这些实体。 entities应包含已下载的所有实体到目前为止 - 不是世界上所有实体。相反,您只能根据分页信息下载您当前正在显示的内容。

  

当我直接导航到/ detail /时,我还没有下载所有用户,所以为了获得这些用户的数据,我将不得不全部下载。数以百万计的用户只需编辑一个。

不,你只要求其中一个。响应操作将触发,负责entities的reducer将此单个实体合并到现有状态。仅仅因为state.entities.users可能包含多个用户并不意味着您需要下载所有这些用户。将entities视为未填充 的缓存。

最后,我将再次指导您从Redux回购中找到“real world” example。它显示了如何为分页信息和实体缓存编写减速器,以及how to normalize JSON in your API responses with normalizr,以便减速器可以轻松地以统一的方式从服务器操作中提取信息。

答案 1 :(得分:0)

在标准化实体之前,我还使用过normalizr方法,但是问题在于这需要手动操作。

如果您了解GraphQL世界中的Apollo,则可能知道它支持自动规范化,因此给定对象的数据不会存储在多个位置。此外,它们还支持自动更新,如果您的服务器响应的对象具有相同的ID但具有更新的属性,则Apollo会识别它并在多个位置更新该对象。

但是,为什么只为GraphQL保留这种奢侈品?由于这个原因,我实现了https://pypi.org/project/Eel/库,该库支持对任何API,REST,GraphQL,Firebase等进行自动归一化。它是如何工作的?假设您有一个图书清单和详细的端点。要与他们进行交流,您只需要分派Redux这样的操作即可:

const fetchBooks = () => ({
  type: FETCH_BOOKS,
  request: { url: '/books' },
  meta: { normalize: true },
});

const fetchBook = id => ({
  type: FETCH_BOOK,
  request: { url: `/books/${id}` },
  meta: { normalize: true },
})

现在,要在两个地方都更新一本书的书名,我们只需这样做:

const updateBookTitle = (id, newTitle) => ({
  type: UPDATE_BOOK_TITLE,
  request: { url: `books/${id}`, method: 'PATCH', data: { newTitle } },
  meta: { normalize: true },
})

如果您对这种方法感兴趣,请阅读redux-requests