Files
hipmi-mobile/hooks/use-paginated-api.ts
bagasbanuna 8a900e9469 Halaman unblock user
Add:
- app/(application)/(user)/profile/[id]/blocked-list.tsx
app/(application)/(user)/profile/[id]/detail-blocked.tsx
components/_ShareComponent/ListEmptyComponent.tsx
components/_ShareComponent/ListLoaderFooterComponent.tsx
components/_ShareComponent/ListSkeletonComponent.tsx
hooks/use-paginated-api.ts
service/api-client/api-blocked.ts

Fix:
modified:   app/(application)/(user)/profile/_layout.tsx
modified:   components/_ShareComponent/NewWrapper.tsx
modified:   components/index.ts
modified:   screens/Profile/ListPage.tsx
modified:   styles/global-styles.ts

### No Issue
2025-11-28 13:55:48 +08:00

98 lines
2.7 KiB
TypeScript

// @/hooks/use-paginated-api.ts
import { useCallback, useState, useEffect, useRef } from "react";
interface UsePaginatedApiProps {
fetcher: (params: { page: number; search?: string }) => Promise<any[]>;
initialSearch?: string; // mengatur nilai awal dari state
pageSize?: number;
dependencies?: any[]; // untuk refresh saat deps berubah (misal: user.id)
}
export const usePaginatedApi = ({
fetcher,
initialSearch = "",
pageSize = 5,
dependencies = [],
}: UsePaginatedApiProps) => {
const [data, setData] = useState<any[]>([]);
const [loading, setLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
const [hasMore, setHasMore] = useState(true);
const [page, setPage] = useState(1);
const [search, setSearch] = useState(initialSearch);
const refreshingRef = useRef<boolean>(false);
const loadingRef = useRef<boolean>(false);
const fetchRef = useRef(false);
const fetchData = useCallback(
async (pageNumber: number, clear: boolean) => {
const isRefresh = clear;
// 🔒 Proteksi: jangan jalankan jika sedang refreshing (untuk refresh)
if (isRefresh && refreshingRef.current) return;
// 🔒 Proteksi: jangan jalankan loadMore jika sedang loading
if (!isRefresh && loadingRef.current) return;
const setLoadingState = (isLoading: boolean) => {
if (isRefresh) {
setRefreshing(isLoading);
refreshingRef.current = isLoading;
} else {
setLoading(isLoading);
loadingRef.current = isLoading;
}
};
setLoadingState(true);
try {
const newData = await fetcher({ page: pageNumber, search });
setData((prev) => {
const current = Array.isArray(prev) ? prev : [];
return clear ? newData : [...current, ...newData];
});
setHasMore(newData.length === pageSize);
setPage(pageNumber);
} catch (error) {
console.error("[usePaginatedApi] Error:", error);
setHasMore(false);
} finally {
setLoadingState(false);
}
},
[search, hasMore, pageSize, ...dependencies]
);
const onRefresh = useCallback(() => {
fetchData(1, true);
}, [fetchData]);
const loadMore = useCallback(() => {
if (hasMore && !loading && !refreshing) {
fetchData(page + 1, false);
}
}, [hasMore, loading, refreshing, page, fetchData]);
// Reset & fetch ulang saat search atau deps berubah
useEffect(() => {
if (fetchRef.current) return; // hindari double initial
fetchRef.current = true;
setPage(1);
setData([]);
setHasMore(true);
fetchData(1, true);
}, [search, ...dependencies]);
return {
data,
loading,
refreshing,
hasMore,
search,
setSearch,
onRefresh,
loadMore,
};
};