带有分页功能的 NgRx 存储

Posted

技术标签:

【中文标题】带有分页功能的 NgRx 存储【英文标题】:NgRx store with pagination 【发布时间】:2020-08-31 21:42:57 【问题描述】:

我正在尝试了解如何在服务器响应分页时设计 NgRx 存储。

如果我们当时只在商店中保留 1 页,那么使用 NgRx 有什么意义呢?

如果我们为所有页面保留插槽并在第一次调用该特定页面时填充存储中的每个页面,那么如何处理 page_size,...查询参数可能因每个请求而异。

如何处理 NgRx 中的过滤和排序。是否值得保留带有太多查询参数的响应?

我现在的界面是这样的

res : 
    pagination: 
        page: 1,
        page_size: 25,
        total_items: 10000,
        total_pages: 400
    ,
    data: [array of 25 objects]

【问题讨论】:

【参考方案1】:

不知道存储pageSize有没有意义,我们可以从当前页面的对象个数中获取。 您可以将页面标识符的实际数据存储在一个单独的属性中,并将所有提取的数据存储在另一个属性中,并且您可以根据需要使用 keys 属性控制 order 元素。 并且在返回到现有数据的页面后,只需更新要同步的日期即可。

你存储

export interface State 
  page: number;
  totalPages: number;
  totalItems: number;
  keys: string[];
  data: 
    [id: string]: DataInterface;
  ;

使用选择器获取数据

export const getCurrentPageDataState = createSelector(getDataState, state => state.keys.map(key => state.data[key]);

【讨论】:

我不确定我是否明白你的意思。这似乎是 NgRx Entity 的一个实现。这是否在商店中当时只保留一页?前任。如果我点击第 2 页,第 1 页的内容会在商店中被替换吗?如果我点击第 1 页然后是第 4 页最后是第 7 页怎么办。如何保持存储内容一致?【参考方案2】:

您好,我会尝试一一回答您的问题,但您是在正确的轨道上

如果我们当时只在商店中保留 1 页,那么使用 NgRx 有什么意义呢? 即使仅针对一个页面,在商店中也有响应的主要原因是在效果或其他组件或服务中使用该搜索结果或其一部分,例如,如果您有多行选择,您将拥有一个选择器,它返回您存储的列表中的选定列表以及处理它们的操作,另一种情况是创建报告的导出按钮,或者也适用于缓存,因此如果您的搜索在选项卡内,如果您切换到另一个,然后回来,您可以从存储中收集所有数据,而不是再次执行它。

它有很多用途,根据我的经验,它可以让你的生活更轻松,但是你可以完美地将它作为组件中的一个本地状态,但是随着时代的发展,我已经这样做了应用程序可能会变得复杂,因为越来越多的与它相关的逻辑进入该组件。 ngrx 的主要目标之一是将您的代码拆分成更易于扩展、维护和测试的部分,组件中的所有逻辑都可以工作,但根据您的案例的复杂程度,维护和测试它们会更加困难。

如果我们为所有页面保留插槽并在第一次调用该特定页面时填充存储中的每个页面,那么如何处理 page_size,...查询参数可能因每个请求而异。

因此,如果您想缓存所有结果,请将它们全部存储在一个数组中,并拥有一个选择器,该选择器通过对起始位置为 currentPage* pageSize 且最后位置为 currentPage* pageSize + 的大数组进行切片来返回当前页面pageSize,每次查询参数更改时,您都必须执行新的搜索并再次存储它,除非搜索结果足够小并且您可以进行内存搜索,这将再次成为过滤主数组的选择器,具体取决于在查询参数上

如何处理 NgRx 中的过滤和排序。是否值得保留带有太多查询参数的响应?

所以这取决于总集合是否很小,那么你可以在内存中进行排序,如果你正在缓存你可以拥有的整个结果,你可以像 SortAction 一样由更改主数组的列触发,如果 in- 则查询参数内存有一个过滤主数组的选择器。

现在,如果搜索太大,通常最好由后端完成搜索,如果他们进行搜索,他们也必须进行排序,所以现在任何排序或查询参数更改都会触发执行的新操作一次搜索,结果是缓存,这里你可以存储一个页面或请求 3 个页面,并使用切片选择器返回你需要的页面,或者如果页面不在缓存中,则返回一个标志,以便你可以触发另一个查询。

还取决于分页控件,您可以实现更巧妙的缓存,如果您可以按照我之前描述的方式向前跳转 x 个页面是最好的方式,但是如果您有分页,他们只能转到下一个和以前喜欢使用 cdk 虚拟滚动,然后每次获得结果时都可以将其附加到当前结果中,因此向后移动是所有缓存,并且假设您请求了 3 页,则仅转发 2 页。

如果您想知道为什么我总是提到缓存 3 页,有研究表明,在分页搜索中,通常超过 90% 的用户不会浏览 3 页,但这不适用于您。 此外,如果您多次实现这一点,您会注意到一些逻辑可以提取到诸如对数组中对象的字段进行排序或对页面进行切片之类的函数中,我一直在计划创建类似于 ngrx 实体的东西,但是更专注于搜索排序和分页,但尚未完成。 希望这会有所帮助。

【讨论】:

这篇文章已经有点老了,但正如你提到的 Ngrx 实体。您如何看待在商店中添加和保留曾经请求的所有内容的策略?这可能无法避免服务器请求,但至少会重新渲染,如果数据集不是太大,应该不是内存问题,对吧? 是的,只要数据集不太大,这是一种可行的方法,但是如果您存储 2000 个项目,在存储中它不会占用太多内存,但在渲染时它们可以,所以我还是用分页来渲染,还是无限滚动

以上是关于带有分页功能的 NgRx 存储的主要内容,如果未能解决你的问题,请参考以下文章

NativeScript + ngrx 存储 + 远程开发工具

Ngrx 存储 take(1),但在注销/登录后重置

如何从 ngrx 存储中获取数据并将其更新到 Observable 变量中?

只有一个减速器的NGRX的多个存储

带有存储过程的服务器端剑道分页

在ngrx/effect中访问存储的正确方法