Infinite Hook Implementation

Purpose

This subtopic addresses the challenge of efficiently fetching and managing paginated or infinite-loading data within React applications. Unlike the core data fetching hook, which handles a single resource, this implementation extends the capability to support multiple pages or "chunks" of data that can be loaded incrementally. It solves problems such as:

This functionality is essential for use cases like infinite scrolling lists, paginated tables, or any scenario where data is retrieved in discrete segments rather than in one bulk request.

Functionality

At its core, the infinite hook implementation is a specialized middleware that wraps the existing core SWR hook to extend its behavior for infinite loading scenarios. It introduces:

Key Code Snippets

const getSnapshot = useCallback(() => {
  const size = isUndefined(get()._l) ? initialSize : get()._l
  return size
}, [cache, infiniteKey, initialSize])

useSyncExternalStore(
  useCallback(
    (callback: () => void) => {
      if (infiniteKey)
        return subscribeCache(infiniteKey, () => {
          callback()
        })
      return () => {}
    },
    [cache, infiniteKey]
  ),
  getSnapshot,
  getSnapshot
)

This snippet shows how the current page size is derived from cache and how React components can subscribe to cache changes to re-render as the page size updates.

for (let i = 0; i < pageSize; ++i) {
  const [pageKey, pageArg] = serialize(
    getKey(i, parallel ? null : previousPageData)
  )
  if (!pageKey) break

  // ...fetch or retrieve cached pageData
  if (fn && shouldFetchPage) {
    await fn(pageArg) // fetch page data
  }
  data[i] = pageData
  if (!parallel) previousPageData = pageData
}

This loop orchestrates fetching each page based on keys until a falsy key is encountered or the designated page size is reached.

const setSize = useCallback(
  (arg: number | ((size: number) => number)) => {
    // calculate new size
    changeSize({ _l: size })
    lastPageSizeRef.current = size

    // recompose page data from cache and trigger mutate
    return mutate(data)
  },
  [infiniteKey, cache, mutate, resolvePageSize]
)

Allows consumers to change how many pages are loaded, triggering cache updates and revalidation as needed.

Integration

This infinite hook middleware integrates tightly with the core SWR hook by wrapping it and augmenting its behavior to support multiple pages instead of a single resource. It complements the parent topic by:

It also coexists with other subtopics such as:

By encapsulating the logic for infinite scrolling data fetching, this subtopic enables developers to efficiently build complex paginated UI components without managing page state or revalidation manually.

Diagram

flowchart TD
  A[Component Calls useSWRInfinite] --> B[Initialize Infinite Key & Cache]
  B --> C[Subscribe to Page Size Changes]
  C --> D{For each page index < size}
  D -->|Generate Page Key| E[Check cache for page data]
  E --> F{Should fetch page?}
  F -->|Yes| G[Fetch page data with fetcher]
  F -->|No| H[Use cached page data]
  G --> I[Update page cache]
  H --> I
  I --> J[Accumulate page data array]
  J --> D
  D -->|All pages fetched| K[Return aggregated data array]
  K --> L[Expose data, error, isValidating, size, setSize, mutate API]
  L --> M[Component renders with data]

This flowchart clarifies the key process of how the infinite hook manages multiple page fetches, cache updates, and exposes an API for consumption.