177 lines
4.7 KiB
JavaScript
177 lines
4.7 KiB
JavaScript
import { useState, useCallback } from 'react'
|
|
import axios from 'axios'
|
|
|
|
export const useTableData = ({
|
|
url,
|
|
pageSize,
|
|
initialPage = 1,
|
|
onDataChange,
|
|
filters = {},
|
|
sorter = {}
|
|
}) => {
|
|
const [pages, setPages] = useState([])
|
|
const [hasMore, setHasMore] = useState(true)
|
|
const [hasPrevious, setHasPrevious] = useState(initialPage > 1)
|
|
const [loading, setLoading] = useState(true)
|
|
const [lazyLoading, setLazyLoading] = useState(false)
|
|
const [totalPages, setTotalPages] = useState(0)
|
|
const [loadedPages, setLoadedPages] = useState([])
|
|
const [loadingPages, setLoadingPages] = useState(new Set())
|
|
const [currentLoadedPageNumber, setCurrentLoadedPageNumber] =
|
|
useState(initialPage)
|
|
|
|
const createSkeletonData = useCallback(() => {
|
|
return Array(pageSize)
|
|
.fill(null)
|
|
.map(() => ({
|
|
_id: `skeleton-${Math.random().toString(36).substring(2, 15)}`,
|
|
isSkeleton: true
|
|
}))
|
|
}, [pageSize])
|
|
|
|
const fetchData = useCallback(
|
|
async (pageNum = 1, append = false, prepend = false) => {
|
|
if (loadingPages.has(pageNum)) {
|
|
return
|
|
}
|
|
|
|
try {
|
|
setLoadingPages((prev) => new Set([...prev, pageNum]))
|
|
|
|
const response = await axios.get(url, {
|
|
params: {
|
|
page: pageNum,
|
|
limit: pageSize,
|
|
...filters,
|
|
sort: sorter.field,
|
|
order: sorter.order
|
|
},
|
|
headers: {
|
|
Accept: 'application/json'
|
|
},
|
|
withCredentials: true
|
|
})
|
|
|
|
const newData = response.data
|
|
const totalCount = parseInt(
|
|
response.headers['x-total-count'] || '0',
|
|
10
|
|
)
|
|
setTotalPages(Math.ceil(totalCount / pageSize))
|
|
|
|
setHasMore(newData.length >= pageSize)
|
|
setHasPrevious(pageNum > 1)
|
|
|
|
if (append) {
|
|
setPages((prev) => {
|
|
const filteredPages = prev.map((page) => ({
|
|
...page,
|
|
items: page.items.filter((item) => !item.isSkeleton)
|
|
}))
|
|
const relevantPages = filteredPages.slice(-2)
|
|
return [...relevantPages, { pageNum, items: newData }]
|
|
})
|
|
setLoadedPages((prev) => {
|
|
const relevantPages = prev.slice(-2)
|
|
return [...relevantPages, pageNum]
|
|
})
|
|
} else if (prepend) {
|
|
setPages((prev) => {
|
|
const filteredPages = prev.map((page) => ({
|
|
...page,
|
|
items: page.items.filter((item) => !item.isSkeleton)
|
|
}))
|
|
const relevantPages = filteredPages.slice(0, 2)
|
|
return [{ pageNum, items: newData }, ...relevantPages]
|
|
})
|
|
setLoadedPages((prev) => {
|
|
const relevantPages = prev.slice(0, 2)
|
|
return [pageNum, ...relevantPages]
|
|
})
|
|
} else {
|
|
setPages([{ pageNum, items: newData }])
|
|
setLoadedPages([pageNum])
|
|
}
|
|
|
|
if (onDataChange) {
|
|
onDataChange(newData)
|
|
}
|
|
|
|
setLoading(false)
|
|
setLazyLoading(false)
|
|
} catch (error) {
|
|
setPages((prev) =>
|
|
prev.map((page) => ({
|
|
...page,
|
|
items: page.items.filter((item) => !item.isSkeleton)
|
|
}))
|
|
)
|
|
setLoading(false)
|
|
setLazyLoading(false)
|
|
throw error
|
|
} finally {
|
|
setLoadingPages((prev) => {
|
|
const newSet = new Set(prev)
|
|
newSet.delete(pageNum)
|
|
return newSet
|
|
})
|
|
}
|
|
},
|
|
[url, pageSize, filters, sorter, onDataChange, loadingPages]
|
|
)
|
|
|
|
const reload = useCallback(() => {
|
|
setCurrentLoadedPageNumber(1)
|
|
setLoadedPages([1])
|
|
return fetchData(1)
|
|
}, [fetchData])
|
|
|
|
const updateData = useCallback((_id, updatedData) => {
|
|
setPages((prevPages) =>
|
|
prevPages.map((page) => ({
|
|
...page,
|
|
items: page.items.map((item) =>
|
|
item._id === _id ? { ...item, ...updatedData } : item
|
|
)
|
|
}))
|
|
)
|
|
}, [])
|
|
|
|
const goToPage = useCallback(
|
|
(pageNum) => {
|
|
if (pageNum > 0 && pageNum <= totalPages) {
|
|
setCurrentLoadedPageNumber(pageNum)
|
|
const pagesToLoad = [pageNum - 1, pageNum, pageNum + 1].filter(
|
|
(p) => p > 0 && p <= totalPages
|
|
)
|
|
setLoadedPages(pagesToLoad)
|
|
return Promise.all(
|
|
pagesToLoad.map((p) => fetchData(p, p > pageNum, p < pageNum))
|
|
)
|
|
}
|
|
},
|
|
[fetchData, totalPages]
|
|
)
|
|
|
|
return {
|
|
pages,
|
|
hasMore,
|
|
hasPrevious,
|
|
loading,
|
|
lazyLoading,
|
|
totalPages,
|
|
loadedPages,
|
|
loadingPages,
|
|
currentLoadedPageNumber,
|
|
createSkeletonData,
|
|
fetchData,
|
|
reload,
|
|
updateData,
|
|
goToPage,
|
|
setCurrentLoadedPageNumber,
|
|
setLazyLoading,
|
|
setPages,
|
|
setLoadedPages
|
|
}
|
|
}
|