react-native-enacton-paginated-list 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +5 -0
- package/README.md +22 -0
- package/dist/PaginatedFlatList.d.ts +12 -0
- package/dist/PaginatedFlatList.js +33 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/types.d.ts +11 -0
- package/dist/types.js +1 -0
- package/dist/usePaginatedList.d.ts +17 -0
- package/dist/usePaginatedList.js +64 -0
- package/package.json +29 -0
package/LICENSE
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# react-native-paginated-list
|
|
2
|
+
|
|
3
|
+
Reusable paginated FlatList and pagination hook for React Native.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
npm install react-native-paginated-list
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
const { data, pagination, isLoading, loadNextPage } =
|
|
13
|
+
usePaginatedList(fetchUsers);
|
|
14
|
+
|
|
15
|
+
<PaginatedFlatList
|
|
16
|
+
data={data}
|
|
17
|
+
pagination={pagination}
|
|
18
|
+
isLoading={isLoading}
|
|
19
|
+
onLoadMore={loadNextPage}
|
|
20
|
+
renderItem={renderItem}
|
|
21
|
+
/>;
|
|
22
|
+
```
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { FlatListProps } from "react-native";
|
|
3
|
+
import { PaginationState } from "./types";
|
|
4
|
+
export interface PaginatedFlatListProps<T> extends Omit<FlatListProps<T>, "onEndReached"> {
|
|
5
|
+
pagination: PaginationState;
|
|
6
|
+
isLoading: boolean;
|
|
7
|
+
onLoadMore: () => void;
|
|
8
|
+
LoaderComponent?: React.ReactNode;
|
|
9
|
+
FooterComponent?: React.ReactNode;
|
|
10
|
+
enableAutoFetch?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function PaginatedFlatList<T>({ data, pagination, isLoading, onLoadMore, LoaderComponent, FooterComponent, enableAutoFetch, ...props }: PaginatedFlatListProps<T>): React.JSX.Element;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React, { useCallback, useMemo } from "react";
|
|
2
|
+
import { FlatList, ActivityIndicator, View } from "react-native";
|
|
3
|
+
export function PaginatedFlatList({ data, pagination, isLoading, onLoadMore, LoaderComponent, FooterComponent, enableAutoFetch = true, ...props }) {
|
|
4
|
+
const safeData = data !== null && data !== void 0 ? data : [];
|
|
5
|
+
const hasMore = useMemo(() => {
|
|
6
|
+
if (pagination.totalPages !== undefined) {
|
|
7
|
+
return pagination.page <= pagination.totalPages;
|
|
8
|
+
}
|
|
9
|
+
if (pagination.totalCount !== undefined) {
|
|
10
|
+
return safeData.length < pagination.totalCount;
|
|
11
|
+
}
|
|
12
|
+
return true;
|
|
13
|
+
}, [pagination, safeData.length]);
|
|
14
|
+
const handleEndReached = useCallback(() => {
|
|
15
|
+
if (!enableAutoFetch || isLoading || !hasMore)
|
|
16
|
+
return;
|
|
17
|
+
onLoadMore();
|
|
18
|
+
}, [enableAutoFetch, isLoading, hasMore, onLoadMore]);
|
|
19
|
+
const renderFooter = () => {
|
|
20
|
+
if (!hasMore)
|
|
21
|
+
return null;
|
|
22
|
+
if (isLoading) {
|
|
23
|
+
return (LoaderComponent !== null && LoaderComponent !== void 0 ? LoaderComponent : (<View style={{ paddingVertical: 16 }}>
|
|
24
|
+
<ActivityIndicator />
|
|
25
|
+
</View>));
|
|
26
|
+
}
|
|
27
|
+
return FooterComponent !== null && FooterComponent !== void 0 ? FooterComponent : null;
|
|
28
|
+
};
|
|
29
|
+
return (<FlatList {...props} data={safeData} onEndReached={handleEndReached} onEndReachedThreshold={0.2} ListFooterComponent={renderFooter} contentContainerStyle={[
|
|
30
|
+
{ paddingBottom: 24 },
|
|
31
|
+
props.contentContainerStyle,
|
|
32
|
+
]}/>);
|
|
33
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
package/dist/types.d.ts
ADDED
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { PaginatedResponse, PaginationState } from "./types";
|
|
2
|
+
interface Options {
|
|
3
|
+
pageSize?: number;
|
|
4
|
+
initialPage?: number;
|
|
5
|
+
autoLoadFirstPage?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function usePaginatedList<T>(fetchPage: (page: number, pageSize: number) => Promise<PaginatedResponse<T>>, options?: Options): {
|
|
8
|
+
data: T[];
|
|
9
|
+
pagination: PaginationState;
|
|
10
|
+
isLoading: boolean;
|
|
11
|
+
error: unknown;
|
|
12
|
+
hasMore: boolean;
|
|
13
|
+
loadNextPage: () => Promise<void>;
|
|
14
|
+
refresh: () => Promise<void>;
|
|
15
|
+
reset: () => void;
|
|
16
|
+
};
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
export function usePaginatedList(fetchPage, options = {}) {
|
|
3
|
+
const { pageSize = 10, initialPage = 1, autoLoadFirstPage = true } = options;
|
|
4
|
+
const [data, setData] = useState([]);
|
|
5
|
+
const [pagination, setPagination] = useState({
|
|
6
|
+
page: initialPage,
|
|
7
|
+
pageSize,
|
|
8
|
+
});
|
|
9
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
10
|
+
const [error, setError] = useState(null);
|
|
11
|
+
const fetchingRef = useRef(false);
|
|
12
|
+
const hasMore = pagination.totalPages !== undefined
|
|
13
|
+
? pagination.page <= pagination.totalPages
|
|
14
|
+
: pagination.totalCount !== undefined
|
|
15
|
+
? data.length < pagination.totalCount
|
|
16
|
+
: true;
|
|
17
|
+
const loadNextPage = useCallback(async () => {
|
|
18
|
+
if (fetchingRef.current || !hasMore)
|
|
19
|
+
return;
|
|
20
|
+
fetchingRef.current = true;
|
|
21
|
+
setIsLoading(true);
|
|
22
|
+
try {
|
|
23
|
+
const res = await fetchPage(pagination.page, pageSize);
|
|
24
|
+
setData((prev) => [...prev, ...res.data]);
|
|
25
|
+
setPagination((prev) => ({
|
|
26
|
+
...prev,
|
|
27
|
+
page: prev.page + 1,
|
|
28
|
+
totalPages: res.totalPages,
|
|
29
|
+
totalCount: res.totalCount,
|
|
30
|
+
}));
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
setError(e);
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
fetchingRef.current = false;
|
|
37
|
+
setIsLoading(false);
|
|
38
|
+
}
|
|
39
|
+
}, [fetchPage, pagination.page, pageSize, hasMore]);
|
|
40
|
+
const reset = useCallback(() => {
|
|
41
|
+
setData([]);
|
|
42
|
+
setPagination({ page: initialPage, pageSize });
|
|
43
|
+
setError(null);
|
|
44
|
+
}, [initialPage, pageSize]);
|
|
45
|
+
const refresh = useCallback(async () => {
|
|
46
|
+
reset();
|
|
47
|
+
await loadNextPage();
|
|
48
|
+
}, [reset, loadNextPage]);
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (autoLoadFirstPage) {
|
|
51
|
+
loadNextPage();
|
|
52
|
+
}
|
|
53
|
+
}, []);
|
|
54
|
+
return {
|
|
55
|
+
data,
|
|
56
|
+
pagination,
|
|
57
|
+
isLoading,
|
|
58
|
+
error,
|
|
59
|
+
hasMore,
|
|
60
|
+
loadNextPage,
|
|
61
|
+
refresh,
|
|
62
|
+
reset,
|
|
63
|
+
};
|
|
64
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-native-enacton-paginated-list",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "Reusable paginated FlatList + pagination hook for React Native",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/manoj-enacton/react-native-paginated-list"
|
|
15
|
+
},
|
|
16
|
+
"peerDependencies": {
|
|
17
|
+
"react": ">=17",
|
|
18
|
+
"react-native": ">=0.68"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc",
|
|
22
|
+
"prepublishOnly": "npm run build"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/react": "^19.2.7",
|
|
26
|
+
"react-native": "^0.83.1",
|
|
27
|
+
"typescript": "^5.9.3"
|
|
28
|
+
}
|
|
29
|
+
}
|