66

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

我正在考虑两种方法以及两者的结合。

共享数据集

  • 我导航到/list,我所有的用户都是从存储在 redux 商店中的 api 下载的,在 key 下users,我还添加了一些 users_offsetusers_limit只呈现列表的一部分
  • 然后我导航到/detail/<id>,并存储currently_selected_user<id>val ... 这意味着我将能够使用类似这样的东西获取用户的数据users.find(res => res.id === currently_selected_user)
  • 更新也很简单,因为我只使用一个数据集和指向它的细节
  • 添加新用户也很容易,同样只需使用相同的用户列表

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

分离的数据集

  • 我导航到/list,而不是从 api 下载我的所有用户,我只下载其中几个,具体取决于我的users_per_pageusers_current_page将设置的内容,我可能会将数据存储为users_currently_visible
  • 然后我导航到/detail/<id>,存储currently_selected_user<id>val ......而不是搜索,users_currently_visible我只是从 api 下载用户的数据 ..
  • 在更新时,我不会users_currently_visible以任何方式更新
  • 我也不会添加

我在这里看到的可能问题是,我必须在访问/list时再次从 api 下载数据,因为它可能与数据库中的内容不同步,我也可能不必要地详细下载用户数据,因为它们可能已经在我的体内users_currently_visible

某种科学怪人的恶作剧

  • 我详细说明,我在分离数据集中执行相同的操作,但不是直接从 api 下载用户数据,而是首先检查:
    • 我有吗users_currently_visible
    • 如果是这样,他们之间是否有使用我的 ID 的用户?如果两者都为真,则我将其用作我的用户数据,否则我进行 api 调用
  • 更新时也会发生同样的情况,我检查我的用户是否存在,users_currently_visible如果存在,我也会更新该列表,如果不存在,我什么也不做

这可能会起作用,但并不觉得这是正确的方法。我可能还需要users_currently_visible在访问时下载新的列表/list,因为我可能已经添加了一个新列表。

有没有粉丝最喜欢的方法?...我敢肯定每个 redux 用户都遇到过同样的事情。

谢谢!

4

2 回答 2

83

请参考Redux repo 中的“真实世界”示例
它显示了这个问题的解决方案。

您的状态形状应如下所示:

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

注意我分别存储entities(ID -> 对象映射)和visibleUsers(当前可见用户的分页状态和 ID 的描述)。

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

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

您不需要全部下载!合并所有下载的实体entities并不意味着您应该查询所有实体。entities应该包含到目前为止已下载的所有实体——不是世界上的所有实体。相反,您只会根据分页信息下载当前显示的内容。

当我直接导航到 /detail/ 时,我还没有下载所有用户,所以要获取一个数据,我必须下载所有用户。数以百万计的用户只是为了编辑一个。

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


最后,我将再次引导您查看Redux 存储库中的“真实世界”示例。它准确地展示了如何为分页信息和实体缓存编写 reducer,以及如何规范化 API 响应中的 JSON,normalizr以便 reducer 可以轻松地以统一的方式从服务器操作中提取信息。

于 2015-11-26T20:33:07.757 回答
0

normalizr之前也使用过方法来规范化实体,但问题是这需要手动工作。

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

但是,为什么这种奢侈应该只保留给 GraphQL?由于这个原因,我实现了redux-requests库,它支持任何 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 },
})

如果您对这种方法感兴趣,可以在此处阅读有关它的更多信息

于 2020-08-14T12:27:07.003 回答