Data Preloading

Overview

The Data Preloading module provides utilities and middleware to fetch and cache data before it is actually needed by a React component. Its primary goal is to reduce loading delays and avoid cascading fetch waterfalls by preemptively populating the cache with data, enabling components to render immediately with cached data or to enter suspense states with minimal delay.

This module is crucial in scenarios where data required by a component can be anticipated and fetched ahead of time, such as during server-side rendering or early client-side initialization. By decoupling data fetching from component mounting, Data Preloading improves perceived performance and user experience.


Core Concepts

Preloading vs Normal Fetching

Cache Coordination

Preloading involves a dedicated cache (PRELOAD) separate from the main cache to track prefetch promises keyed by serialized request keys. When the component later calls useSWR, the middleware checks this PRELOAD cache and serves the preloaded promise to avoid duplicate requests.


How Preloading Works

Preload Utility Function

The main function preload accepts a key and a fetcher function. It serializes the key to a stable string and checks if a preload request for this key already exists in the preload cache.

This behavior ensures idempotent preloading: multiple calls to preload with the same key before the component uses the data will share the same fetch promise.

export const preload = <Data, SWRKey, Fetcher>(
  key_: SWRKey,
  fetcher: Fetcher
): ReturnType<Fetcher> => {
  const [key, fnArg] = serialize(key_)
  const [, , , PRELOAD] = SWRGlobalState.get(cache) as GlobalState

  if (PRELOAD[key]) return PRELOAD[key]

  const req = fetcher(fnArg) as ReturnType<Fetcher>
  PRELOAD[key] = req
  return req
}

Preload Middleware

To integrate preloading with the normal SWR lifecycle, a middleware is provided. This middleware wraps the fetcher function used by useSWR:

This ensures that preloaded data is consumed by the first hook that uses it, and subsequent hooks will either reuse cached data or fetch fresh data.

export const middleware: Middleware =
  useSWRNext => (key_, fetcher_, config) => {
    const fetcher =
      fetcher_ &&
      ((...args: any[]) => {
        const [key] = serialize(key_)
        const [, , , PRELOAD] = SWRGlobalState.get(cache) as GlobalState

        if (key.startsWith(INFINITE_PREFIX)) {
          return fetcher_(...args)
        }

        const req = PRELOAD[key]
        if (isUndefined(req)) return fetcher_(...args)
        delete PRELOAD[key]
        return req
      })
    return useSWRNext(key_, fetcher, config)
  }

Interaction with Other Modules


Important Design Patterns and Concepts


Summary of Key Files and Their Roles

File

Role

src/_internal/utils/preload.ts

Implements the preload utility function and the preload middleware for SWR integration.


Mermaid Diagram: Preloading Workflow Sequence

This sequence diagram visualizes the typical workflow of preloading data and its consumption by a component using the SWR hook with preload middleware.

sequenceDiagram
    participant Preloader as Preload Utility
    participant Cache as PRELOAD Cache
    participant Component as React Component
    participant SWRHook as useSWR Hook with Preload Middleware
    participant Fetcher as Fetcher Function

    Preloader->>Cache: Check if preload promise exists for key
    alt preload promise exists
        Cache-->>Preloader: Return existing preload promise
    else
        Preloader->>Fetcher: Call fetcher with serialized key arg
        Fetcher-->>Preloader: Return fetch promise
        Preloader->>Cache: Store preload promise by key
    end

    Component->>SWRHook: Call useSWR with key and fetcher
    SWRHook->>Cache: Check for preload promise by key
    alt preload promise exists
        Cache-->>SWRHook: Return preload promise and delete from cache
    else
        SWRHook->>Fetcher: Call fetcher normally
    end
    SWRHook-->>Component: Return data or promise

Summary

The Data Preloading module enables anticipatory data fetching by caching fetch promises before component mount and transparently supplying them during component data fetching. Using a dedicated preload cache and middleware integration, it reduces loading delays and improves rendering performance without disrupting the SWR hook’s normal lifecycle or infinite loading mechanisms. This design supports efficient, idempotent preloading with clean separation of concerns and extensibility.