perf: optimize load of pagination items with caching

This commit is contained in:
Divlo
2022-08-24 17:22:55 +02:00
parent 19fc29ad47
commit ad64f1c571
16 changed files with 168 additions and 65 deletions

View File

@ -2,6 +2,11 @@ import { useState, useRef, useCallback } from 'react'
import { AxiosInstance } from 'axios'
import { FetchState } from './useFetchState'
import {
CacheKey,
getPaginationCache,
savePaginationCache
} from '../tools/cache'
export interface Query {
[key: string]: string
@ -13,6 +18,11 @@ export interface UsePaginationOptions {
api: AxiosInstance
url: string
inverse?: boolean
cacheKey?: CacheKey
}
export interface PaginationItem {
id: number
}
export type SetItems<T> = React.Dispatch<React.SetStateAction<T[]>>
@ -25,16 +35,25 @@ export interface UsePaginationResult<T> {
setItems: SetItems<T>
}
export const usePagination = <T extends { id: number }>(
export const usePagination = <T extends PaginationItem>(
options: UsePaginationOptions
): UsePaginationResult<T> => {
const { api, url, inverse = false } = options
const { api, url, inverse = false, cacheKey } = options
const [items, setItems] = useState<T[]>([])
const [hasMore, setHasMore] = useState(true)
const fetchState = useRef<FetchState>('idle')
const afterId = useRef<number | null>(null)
const updateAfterId = (newItems: T[]): void => {
if (!inverse) {
afterId.current =
newItems.length > 0 ? newItems[newItems.length - 1].id : null
} else {
afterId.current = newItems.length > 0 ? newItems[0].id : null
}
}
const nextPageAsync: NextPageAsync = useCallback(
async (query) => {
if (fetchState.current !== 'idle') {
@ -49,19 +68,22 @@ export const usePagination = <T extends { id: number }>(
const { data: newItems } = await api.get<T[]>(
`${url}?${searchParameters.toString()}`
)
if (!inverse) {
afterId.current =
newItems.length > 0 ? newItems[newItems.length - 1].id : null
} else {
afterId.current = newItems.length > 0 ? newItems[0].id : null
}
updateAfterId(newItems)
setItems((oldItems) => {
return inverse ? [...newItems, ...oldItems] : [...oldItems, ...newItems]
const updatedItems = inverse
? [...newItems, ...oldItems]
: [...oldItems, ...newItems]
if (cacheKey != null) {
savePaginationCache(cacheKey, updatedItems)
}
return updatedItems
})
setHasMore(newItems.length > 0)
fetchState.current = 'idle'
},
[api, url, inverse]
// eslint-disable-next-line react-hooks/exhaustive-deps -- We don't want infinite loops with updateAfterId
[api, url, inverse, cacheKey]
)
const nextPage: NextPage = useCallback(
@ -80,9 +102,17 @@ export const usePagination = <T extends { id: number }>(
)
const resetPagination = useCallback((): void => {
afterId.current = null
setItems([])
}, [])
if (cacheKey == null) {
afterId.current = null
setItems([])
} else {
const newItems = getPaginationCache<T>(cacheKey)
setItems(newItems)
updateAfterId(newItems)
}
// eslint-disable-next-line react-hooks/exhaustive-deps -- We don't want infinite loops with updateAfterId
}, [cacheKey])
return { items, hasMore, nextPage, resetPagination, setItems }
}