import { useRef , useState , useCallback } from 'react' ; import useSWR , { SWRConfig , SWRHook , MutatorCallback , Middleware , BareFetcher } from 'swr' ; import { useIsomorphicLayoutEffect } from '../src/utils/env' ; import { serialize } from '../src/utils/serialize' ; import { isUndefined , isFunction , UNDEFINED } from '../src/utils/helper' ; import { withMiddleware } from '../src/utils/with-middleware' ; import type { SWRInfiniteConfiguration , SWRInfiniteResponse , SWRInfiniteHook , SWRInfiniteKeyLoader , SWRInfiniteFetcher } from './types' ; const INFINITE_PREFIX = '$inf$' ; const getFirstPageKey = ( getKey ) => { return serialize ( getKey ? getKey ( 0 , null ) : null ) [ 0 ] ; } ; export const unstable_serialize = ( getKey ) => { return INFINITE_PREFIX + getFirstPageKey ( getKey ) ; } ; export const infinite = ( < Data , Error > ( useSWRNext ) => ( getKey , fn , config ) : SWRInfiniteResponse < Data , Error > => { const rerender = useState ( { } ) [ 1 ] ; const didMountRef = useRef < boolean > ( false ) ; const dataRef = useRef < Data [ ] > ( ) ; const { cache , initialSize = 1 , revalidateAll = false , persistSize = false , revalidateFirstPage = true } = config ; let firstPageKey = null ; try { firstPageKey = getFirstPageKey ( getKey ) ; } catch ( err ) { } let contextCacheKey = null ; let pageSizeCacheKey = null ; if ( firstPageKey ) { contextCacheKey = '$ctx$' + firstPageKey ; pageSizeCacheKey = '$len$' + firstPageKey ; } const resolvePageSize = useCallback ( ( ) : number => { const cachedPageSize = cache . get ( pageSizeCacheKey ) ; return isUndefined ( cachedPageSize ) ? initialSize : cachedPageSize ; } , [ pageSizeCacheKey , initialSize ] ) ; const lastPageSizeRef = useRef < number > ( resolvePageSize ( ) ) ; useIsomorphicLayoutEffect ( ( ) => { if ( ! didMountRef . current ) { didMountRef . current = true ; return ; } if ( firstPageKey ) { cache . set ( pageSizeCacheKey , persistSize ? lastPageSizeRef . current : initialSize ) ; } } , [ firstPageKey ] ) ; const swr = useSWRNext < Data [ ] , Error > ( firstPageKey ? INFINITE_PREFIX + firstPageKey : null , async ( ) => { const [ forceRevalidateAll , originalData ] = cache . get ( contextCacheKey ) || [ ] ; const data = [ ] ; const pageSize = resolvePageSize ( ) ; let previousPageData = null ; for ( let i = 0 ; i < pageSize ; ++ i ) { const [ pageKey , pageArgs ] = serialize ( getKey ( i , previousPageData ) ) ; if ( ! pageKey ) { break ; } let pageData = cache . get ( pageKey ) ; const shouldFetchPage = revalidateAll || forceRevalidateAll || isUndefined ( pageData ) || ( revalidateFirstPage && ! i && ! isUndefined ( dataRef . current ) ) || ( originalData && ! isUndefined ( originalData [ i ] ) && ! config . compare ( originalData [ i ] , pageData ) ) ; if ( fn && shouldFetchPage ) { pageData = await fn ( ... pageArgs ) ; cache . set ( pageKey , pageData ) ; } data . push ( pageData ) ; previousPageData = pageData ; } cache . delete ( contextCacheKey ) ; return data ; } , config ) ; useIsomorphicLayoutEffect ( ( ) => { dataRef . current = swr . data ; } , [ swr . data ] ) ; const mutate = useCallback ( ( ... args ) => { const data = args [ 0 ] ; const shouldRevalidate = args [ 1 ] !== false ; if ( ! contextCacheKey ) return ; if ( shouldRevalidate ) { if ( ! isUndefined ( data ) ) { const originalData = dataRef . current ; cache . set ( contextCacheKey , [ false , originalData ] ) ; } else { cache . set ( contextCacheKey , [ true ] ) ; } } return args . length ? swr . mutate ( data , shouldRevalidate ) : swr . mutate ( ) ; } , [ contextCacheKey ] ) ; const resolvePagesFromCache = ( pageSize ) : Data [ ] | undefined => { const data = [ ] ; let previousPageData = null ; for ( let i = 0 ; i < pageSize ; ++ i ) { const [ pageKey ] = serialize ( getKey ( i , previousPageData ) ) ; const pageData = pageKey ? cache . get ( pageKey ) : UNDEFINED ; if ( isUndefined ( pageData ) ) return dataRef . current ; data . push ( pageData ) ; previousPageData = pageData ; } return data ; } ; const setSize = useCallback ( ( arg ) => { if ( ! pageSizeCacheKey ) return ; let size ; if ( isFunction ( arg ) ) { size = arg ( resolvePageSize ( ) ) ; } else if ( typeof arg == 'number' ) { size = arg ; } if ( typeof size != 'number' ) return ; cache . set ( pageSizeCacheKey , size ) ; lastPageSizeRef . current = size ; rerender ( { } ) ; return mutate ( resolvePagesFromCache ( size ) ) ; } , [ pageSizeCacheKey , resolvePageSize , mutate ] ) ; return { size : resolvePageSize ( ) , setSize , mutate , get error ( ) { return swr . error ; } , get data ( ) { return swr . data ; } , get isValidating ( ) { return swr . isValidating ; } } as SWRInfiniteResponse < Data , Error > ; } ) as unknown as Middleware ; export default withMiddleware ( useSWR , infinite ) as SWRInfiniteHook ; export { SWRInfiniteConfiguration , SWRInfiniteResponse , SWRInfiniteHook , SWRInfiniteKeyLoader , SWRInfiniteFetcher } ; deprecated export type InfiniteFetcher = SWRInfiniteFetcher ;