-
Notifications
You must be signed in to change notification settings - Fork 29
/
useInfiniteScrollListing.ts
121 lines (102 loc) · 2.88 KB
/
useInfiniteScrollListing.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import { useEffect, useMemo, useRef, useState } from 'react';
import { equals, gte, isNil, reduce } from 'ramda';
import { PrimitiveAtom, useAtom } from 'jotai';
import { JsonDecoder } from 'ts.data.json';
import {
QueryParameter,
buildListingEndpoint,
useFetchQuery,
useIntersectionObserver
} from '@centreon/ui';
import type { Listing } from '../api/models';
import { Parameters } from '../api/buildListingEndpoint/models';
interface UseInfiniteScrollListing<T> {
elementRef: (node) => void;
elements: Array<T>;
isLoading: boolean;
total?: number;
}
interface UseInfiniteScrollListingProps<T> {
customQueryParameters?: Array<QueryParameter>;
decoder?: JsonDecoder.Decoder<Listing<T>>;
endpoint: string;
limit?: number;
pageAtom: PrimitiveAtom<number>;
parameters?: Parameters;
queryKeyName: string;
suspense?: boolean;
}
export const useInfiniteScrollListing = <T>({
queryKeyName,
endpoint,
decoder,
pageAtom,
suspense = true,
parameters,
customQueryParameters,
limit = 100
}: UseInfiniteScrollListingProps<T>): UseInfiniteScrollListing<T> => {
const [maxPage, setMaxPage] = useState(1);
const elements = useRef<Array<T> | undefined>(undefined);
const [page, setPage] = useAtom(pageAtom);
const { data, isLoading, prefetchNextPage, fetchStatus } = useFetchQuery<
Listing<T>
>({
decoder,
getEndpoint: (params) =>
buildListingEndpoint({
baseEndpoint: endpoint,
customQueryParameters,
parameters: { limit, page: params?.page || page, ...parameters }
}),
getQueryKey: () => [queryKeyName, page],
isPaginated: true,
queryOptions: {
refetchOnMount: false,
refetchOnWindowFocus: false,
suspense: suspense && equals(page, 1)
}
});
const elementRef = useIntersectionObserver({
action: () => {
setPage((currentPage) => currentPage + 1);
},
loading: !equals(fetchStatus, 'idle'),
maxPage,
page
});
elements.current = useMemo(() => {
if (isNil(data) || !equals(fetchStatus, 'idle')) {
return elements.current;
}
return reduce<T, Array<T>>(
(acc, element) => [...acc, element],
equals(page, 1) ? [] : elements.current || [],
data.result
);
}, [page, data, fetchStatus]);
useEffect(() => {
if (isNil(data)) {
return;
}
const total = data.meta.total || 1;
const newMaxPage = Math.ceil(total / limit);
setMaxPage(newMaxPage);
if (gte(page, newMaxPage)) {
return;
}
prefetchNextPage({
getPrefetchQueryKey: (newPage) => [`dashboards`, newPage],
page
});
}, [data]);
useEffect(() => {
return () => setPage(1);
}, []);
return {
elementRef,
elements: elements.current || [],
isLoading,
total: data?.meta.total
};
};