Skip to content

Commit

Permalink
type(infinite): export SWRInfinteHook and InfiniteKeyLoader from infi…
Browse files Browse the repository at this point in the history
…nite (vercel#1714)

* type: export SWRInfinteHook and InfiniteKeyLoader from infinite

* rename InfiniteKeyLoader and InfiniteFetcher
  • Loading branch information
houkanshan authored and nevilm-lt committed Apr 22, 2022
1 parent 2cc7217 commit c71c0ba
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 85 deletions.
95 changes: 36 additions & 59 deletions infinite/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,74 +18,45 @@ import {
SWRInfiniteConfiguration,
SWRInfiniteResponse,
SWRInfiniteHook,
InfiniteKeyLoader,
InfiniteFetcher
SWRInfiniteKeyLoader,
SWRInfiniteFetcher
} from './types'

const INFINITE_PREFIX = '$inf$'

const getFirstPageKey = (getKey: InfiniteKeyLoader) => {
const getFirstPageKey = (getKey: SWRInfiniteKeyLoader) => {
return serialize(getKey ? getKey(0, null) : null)[0]
}

export const unstable_serialize = (getKey: InfiniteKeyLoader) => {
export const unstable_serialize = (getKey: SWRInfiniteKeyLoader) => {
return INFINITE_PREFIX + getFirstPageKey(getKey)
}

export const infinite = ((<Data, Error, Args extends Arguments>(
useSWRNext: SWRHook
) => (
getKey: InfiniteKeyLoader<Args>,
fn: Fetcher<Data> | null,
config: typeof SWRConfig.default & SWRInfiniteConfiguration<Data, Error, Args>
): SWRInfiniteResponse<Data, Error> => {
const rerender = useState({})[1]
const didMountRef = useRef<boolean>(false)
const dataRef = useRef<Data[]>()

const {
cache,
initialSize = 1,
revalidateAll = false,
persistSize = false
} = config

// The serialized key of the first page.
let firstPageKey: string | null = null
try {
firstPageKey = getFirstPageKey(getKey)
} catch (err) {
// not ready
}

// We use cache to pass extra info (context) to fetcher so it can be globally
// shared. The key of the context data is based on the first page key.
let contextCacheKey: string | null = null

// Page size is also cached to share the page data between hooks with the
// same key.
let pageSizeCacheKey: string | null = null

if (firstPageKey) {
contextCacheKey = '$ctx$' + firstPageKey
pageSizeCacheKey = '$len$' + firstPageKey
}

const resolvePageSize = useCallback((): number => {
const cachedPageSize = cache.get(pageSizeCacheKey)
return isUndefined(cachedPageSize) ? initialSize : cachedPageSize

// `cache` isn't allowed to change during the lifecycle
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [pageSizeCacheKey, initialSize])
// keep the last page size to restore it with the persistSize option
const lastPageSizeRef = useRef<number>(resolvePageSize())

// When the page key changes, we reset the page size if it's not persisted
useIsomorphicLayoutEffect(() => {
if (!didMountRef.current) {
didMountRef.current = true
return
export const infinite = (<Data, Error>(useSWRNext: SWRHook) =>
(
getKey: SWRInfiniteKeyLoader,
fn: BareFetcher<Data> | null,
config: Omit<typeof SWRConfig.default, 'fetcher'> &
Omit<SWRInfiniteConfiguration<Data, Error>, 'fetcher'>
): 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

// The serialized key of the first page.
let firstPageKey: string | null = null
try {
firstPageKey = getFirstPageKey(getKey)
} catch (err) {
// not ready
}

if (firstPageKey) {
Expand Down Expand Up @@ -255,4 +226,10 @@ export const infinite = ((<Data, Error, Args extends Arguments>(
}) as unknown) as Middleware

export default withMiddleware(useSWR, infinite) as SWRInfiniteHook
export { SWRInfiniteConfiguration, SWRInfiniteResponse, InfiniteFetcher }
export {
SWRInfiniteConfiguration,
SWRInfiniteResponse,
SWRInfiniteHook,
SWRInfiniteKeyLoader,
SWRInfiniteFetcher
}
110 changes: 84 additions & 26 deletions infinite/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,31 @@ import { SWRConfiguration, SWRResponse, Arguments } from 'swr'

type FetcherResponse<Data = unknown> = Data | Promise<Data>

export type InfiniteFetcher<
Args extends Arguments = Arguments,
Data = any
> = Args extends (readonly [...infer K])
? ((...args: [...K]) => FetcherResponse<Data>)
: Args extends null
? never
: Args extends (infer T)
? (...args: [T]) => FetcherResponse<Data>
export type SWRInfiniteFetcher<
Data = any,
KeyLoader extends SWRInfiniteKeyLoader = SWRInfiniteKeyLoader
> = KeyLoader extends (...args: any[]) => any
? ReturnType<KeyLoader> extends
| readonly [...infer K]
| null
| false
| undefined
? (...args: [...K]) => FetcherResponse<Data>
: ReturnType<KeyLoader> extends infer T | null | false | undefined
? (...args: [T]) => FetcherResponse<Data>
: never
: never

export type InfiniteKeyLoader<Args extends Arguments = Arguments> =
| ((index: number, previousPageData: any | null) => Args)
| null
export type SWRInfiniteKeyLoader = (
index: number,
previousPageData: any | null
) => Arguments

export type SWRInfiniteConfiguration<
Data = any,
Error = any,
Args extends Arguments = Arguments
> = SWRConfiguration<Data[], Error, Args> & {
Fn extends SWRInfiniteFetcher<Data> = BareFetcher<Data>
> extends SWRConfiguration<Data[], Error> {
initialSize?: number
revalidateAll?: boolean
persistSize?: boolean
Expand All @@ -36,24 +41,77 @@ export interface SWRInfiniteResponse<Data = any, Error = any>
}

export interface SWRInfiniteHook {
<Data = any, Error = any, SWRInfiniteArguments extends Arguments = Arguments>(
getKey: InfiniteKeyLoader<SWRInfiniteArguments>
<
Data = any,
Error = any,
KeyLoader extends SWRInfiniteKeyLoader = (
index: number,
previousPageData: Data | null
) => null
>(
getKey: KeyLoader
): SWRInfiniteResponse<Data, Error>
<Data = any, Error = any, SWRInfiniteArguments extends Arguments = Arguments>(
getKey: InfiniteKeyLoader<SWRInfiniteArguments>,
fetcher: InfiniteFetcher<SWRInfiniteArguments, Data> | null
<
Data = any,
Error = any,
KeyLoader extends SWRInfiniteKeyLoader = (
index: number,
previousPageData: Data | null
) => null
>(
getKey: KeyLoader,
fetcher: SWRInfiniteFetcher<Data, KeyLoader> | null
): SWRInfiniteResponse<Data, Error>
<Data = any, Error = any, SWRInfiniteArguments extends Arguments = Arguments>(
getKey: InfiniteKeyLoader<SWRInfiniteArguments>,
<
Data = any,
Error = any,
KeyLoader extends SWRInfiniteKeyLoader = (
index: number,
previousPageData: Data | null
) => null
>(
getKey: KeyLoader,
config:
| SWRInfiniteConfiguration<Data, Error, SWRInfiniteArguments>
| SWRInfiniteConfiguration<
Data,
Error,
SWRInfiniteFetcher<Data, KeyLoader>
>
| undefined
): SWRInfiniteResponse<Data, Error>
<Data = any, Error = any, SWRInfiniteArguments extends Arguments = Arguments>(
getKey: InfiniteKeyLoader<SWRInfiniteArguments>,
fetcher: InfiniteFetcher<SWRInfiniteArguments, Data> | null,
<
Data = any,
Error = any,
KeyLoader extends SWRInfiniteKeyLoader = (
index: number,
previousPageData: Data | null
) => null
>(
getKey: KeyLoader,
fetcher: SWRInfiniteFetcher<Data, KeyLoader> | null,
config:
| SWRInfiniteConfiguration<Data, Error, SWRInfiniteArguments>
| SWRInfiniteConfiguration<
Data,
Error,
SWRInfiniteFetcher<Data, KeyLoader>
>
| undefined
): SWRInfiniteResponse<Data, Error>
<Data = any, Error = any>(getKey: SWRInfiniteKeyLoader): SWRInfiniteResponse<
Data,
Error
>
<Data = any, Error = any>(
getKey: SWRInfiniteKeyLoader,
fetcher: BareFetcher<Data> | null
): SWRInfiniteResponse<Data, Error>
<Data = any, Error = any>(
getKey: SWRInfiniteKeyLoader,
config: SWRInfiniteConfiguration<Data, Error, BareFetcher<Data>> | undefined
): SWRInfiniteResponse<Data, Error>
<Data = any, Error = any>(
getKey: SWRInfiniteKeyLoader,
fetcher: BareFetcher<Data> | null,
config: SWRInfiniteConfiguration<Data, Error, BareFetcher<Data>> | undefined
): SWRInfiniteResponse<Data, Error>
}

0 comments on commit c71c0ba

Please sign in to comment.