use-grid-data.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. import { useEffect, useState, useMemo } from 'react';
  2. import { useTranslation } from 'react-i18next';
  3. import { useInfiniteQuery, useQueryClient } from '@tanstack/react-query';
  4. import { flattenPagesForUniList } from 'app/lib/browse-query';
  5. import {
  6. getDropdownFilterKeys,
  7. getNumberedFilterKeys,
  8. getQueryParamFilterKeys,
  9. buildQueryAppend,
  10. buildFilterParam,
  11. } from './filters';
  12. import { fetchGridData } from './actions';
  13. import { useGridApi } from './context';
  14. /**
  15. * Everything the grid needs before it can render: rows, filter/search state and
  16. * the paginated query. Column layout is computed once in `GridProvider`.
  17. *
  18. * The query cache is the only copy of the rows; store handlers patch it through
  19. * the `queryClient` / `queryKey` injected below.
  20. */
  21. export function useGridData(data) {
  22. const { t } = useTranslation();
  23. const gridStore = useGridApi();
  24. const queryClient = useQueryClient();
  25. const settings = data.settings;
  26. const [selectedFilters, setSelectedFilters] = useState({});
  27. const [searchValue, setSearchValue] = useState('');
  28. const dropdownFilterKeys = useMemo(
  29. () => getDropdownFilterKeys(settings.filters),
  30. [settings.filters]
  31. );
  32. const numberedFilterKeys = useMemo(
  33. () => getNumberedFilterKeys(dropdownFilterKeys),
  34. [dropdownFilterKeys]
  35. );
  36. const queryParamFilterKeys = useMemo(
  37. () => getQueryParamFilterKeys(dropdownFilterKeys),
  38. [dropdownFilterKeys]
  39. );
  40. const queryAppend = useMemo(
  41. () => buildQueryAppend(settings, queryParamFilterKeys, selectedFilters),
  42. [settings?.query_append, queryParamFilterKeys, selectedFilters]
  43. );
  44. const filterParam = useMemo(
  45. () => buildFilterParam(numberedFilterKeys, selectedFilters, searchValue),
  46. [numberedFilterKeys, selectedFilters, searchValue]
  47. );
  48. const queryKey = useMemo(
  49. () => ['grid', settings.object, filterParam, JSON.stringify(queryAppend)],
  50. [settings.object, filterParam, queryAppend]
  51. );
  52. const {
  53. status,
  54. data: pagesData,
  55. fetchNextPage,
  56. hasNextPage,
  57. isFetchingNextPage,
  58. refetch: reloadGrid,
  59. isRefetching,
  60. } = useInfiniteQuery({
  61. queryKey,
  62. queryFn: ({ pageParam }) => fetchGridData({
  63. pageParam,
  64. settings,
  65. selectedFilters,
  66. numberedFilterKeys,
  67. queryAppend,
  68. searchValue
  69. }),
  70. getNextPageParam: (lastPage) => {
  71. if (lastPage?.data.length > 0) {
  72. return {
  73. start: lastPage.params.start + lastPage.params.per_page
  74. };
  75. }
  76. return undefined;
  77. },
  78. staleTime: 1000,
  79. initialPageParam: { start: 0 }
  80. });
  81. /** Store handlers read these through `get()` — nothing subscribes to them. */
  82. useEffect(() => {
  83. gridStore.setState({ settings, queryAppend, queryClient, queryKey, reloadGrid, t });
  84. }, [gridStore, settings, queryAppend, queryClient, queryKey, reloadGrid, t]);
  85. const rows = useMemo(() => flattenPagesForUniList(pagesData), [pagesData]);
  86. const handleEndReached = () => {
  87. if (!hasNextPage || isFetchingNextPage) return;
  88. fetchNextPage();
  89. };
  90. /** Search is part of the query key via `filterParam` — no extra reload. */
  91. const handleSearch = (value) => {
  92. setSearchValue(value);
  93. };
  94. /** Dropdown filter is part of the query key via `filterParam` / `queryAppend`. */
  95. const handleFilter = (filterKey, value) => {
  96. setSelectedFilters((prev) => ({ ...prev, [filterKey]: value }));
  97. };
  98. return {
  99. settings,
  100. rows,
  101. dropdownFilterKeys,
  102. selectedFilters,
  103. handleFilter,
  104. handleSearch,
  105. /** Everything the table needs to render loading / refresh / paging states. */
  106. query: {
  107. status,
  108. hasNextPage,
  109. isFetchingNextPage,
  110. isRefetching,
  111. handleEndReached,
  112. reloadGrid,
  113. },
  114. };
  115. }