index.ts
Overview
The index.ts file in the subscription module of the SWR (stale-while-revalidate) ecosystem implements middleware and hooks that enable React components to subscribe to external real-time data sources. It extends the core SWR hook functionality by adding subscription management with reference counting and lifecycle controls. This allows components to receive continuous updates from external data channels (e.g., WebSocket streams, event emitters) while sharing subscription instances and efficiently managing resources.
The file exports a default hook, useSWRSubscription, which abstracts away the subscription lifecycle and integration with the SWR cache, providing a simple and declarative API for real-time data consumption in React applications.
Detailed Explanation
Key Concepts
Subscription Middleware: A higher-order function that wraps a SWR hook and manages subscription lifecycles, reference counting, data/error propagation, and resource cleanup.
Reference Counting: Tracks how many subscribers are listening to a particular subscription key to share a single underlying subscription.
Subscription Storage: Uses a
WeakMapkeyed by SWR cache instance to store subscription states isolated per cache boundary.Reactive Data Updates: Propagates data and error updates from the external source into SWR's cache, triggering React re-renders.
Entities in the File
1. Type Alias: SubscriptionStates
type SubscriptionStates = [Map<string, number>, Map<string, () => void>]
Tuple containing two maps:
The first map tracks subscription keys to the number of active subscribers (reference count).
The second map tracks subscription keys to their disposer functions (used to unsubscribe).
2. Constant: subscriptionStorage
const subscriptionStorage = new WeakMap<object, SubscriptionStates>()
A
WeakMapkeyed by the SWR cache object.Ensures subscription states are scoped per cache boundary.
Prevents memory leaks as keys (cache objects) can be garbage collected.
3. Constant: SUBSCRIPTION_PREFIX
const SUBSCRIPTION_PREFIX = '$sub$'
A string prefix to namespace subscription keys in the SWR cache.
Prevents key collisions between subscription keys and regular SWR keys.
4. Middleware Function: subscription
export const subscription = (<Data = any, Error = any>(useSWRNext: SWRHook) =>
(
_key: Key,
subscribe: SWRSubscription<any, Data, Error>,
config: SWRConfiguration & typeof SWRConfig.defaultValue
): SWRSubscriptionResponse<Data, Error> => { ... }
) as unknown as Middleware
Purpose
This is the core middleware that wraps the base SWR hook (
useSWRNext) to add subscription capabilities.Manages subscription lifecycle, reference counting, data/error updates, and disposal.
Parameters
_key: Key
The unique identifier for the subscription. Could be a string, array, or any serializable value.subscribe: SWRSubscription<any, Data, Error>
A user-provided function that sets up the external subscription. It receives the subscription arguments and anextcallback for pushing updates.config: SWRConfiguration & typeof SWRConfig.defaultValue
SWR configuration options including cache, revalidation options, etc.
Return Value
SWRSubscriptionResponse<Data, Error>
An object exposing reactive getters:.data: The current data from the subscription..error: The current error state.
Function Workflow
Key Serialization and Prefixing
The_keyis serialized into[key, args]. Thekeyis prefixed withSUBSCRIPTION_PREFIXto avoid cache conflicts.Initialize Subscription Storage
If thesubscriptionStoragedoes not have an entry for the specified cache, it initializes two maps—one for subscription counts, one for disposers.Retrieve Subscription State Maps
Extracts the subscriptions map and disposers map for the current cache.Use Isomorphic Layout Effect
A React hook (useIsomorphicLayoutEffect) runs on mount and unmount of components using the subscription:On mount:
Increment the reference count for the subscription key.
If this is the first subscriber (
refCount === 0), call thesubscribefunction with the arguments and anextcallback.Store the returned
disposefunction for later unsubscription.
On unmount:
Decrement the reference count.
If no subscribers remain (
count === 0), call thedisposefunction to unsubscribe.
nextCallback for Data/Error Updates
Passed tosubscribe, it receives(error, data)from the external source and updates the SWR cache accordingly:If an error is present, it sets the error in cache.
Otherwise, it clears the error and mutates the SWR cache with new data.
Return Reactive Data and Error
Returns an object withdataanderrorgetters that reflect the current SWR cache state.
5. Hook: useSWRSubscription
const useSWRSubscription = withMiddleware(
useSWR,
subscription
) as SWRSubscriptionHook
Purpose
A user-facing React hook that combines the core SWR hook with the subscription middleware.
Enables components to declaratively subscribe to real-time external data sources.
Usage Example
import useSWRSubscription from 'swr/subscription'
const { data, error } = useSWRSubscription(key, (key, { next }) => {
const unsubscribe = dataSource.subscribe(key, (err, data) => {
next(err, data)
})
return unsubscribe
})
key: Subscription identifier.subscribe: A function that sets up the external subscription and returns an unsubscribe function.The hook returns an object with
dataanderrorreflecting the subscription's state.
6. Exported Types
export type {
SWRSubscription,
SWRSubscriptionOptions,
SWRSubscriptionResponse,
SWRSubscriptionHook
}
Type definitions related to subscriptions are re-exported for external use.
Important Implementation Details
Subscription Reference Counting
Prevents redundant subscriptions by sharing a single external subscription among multiple React components subscribing to the same key.Cache Boundary Isolation
Using aWeakMapkeyed by the SWR cache ensures that subscriptions are scoped and do not interfere across different SWR cache instances, supporting multiple SWR contexts in an application.Isomorphic Effect Hook
useIsomorphicLayoutEffectensures subscription setup and teardown is consistent between client and server environments, avoiding hydration mismatches.Subscription Key Namespacing
Prefixing subscription keys with a unique string ($sub$) isolates subscription cache entries from normal SWR entries.Mandatory Unsubscriber Return
Thesubscribefunction must return a function to unsubscribe; if it does not, an error is thrown to prevent potential resource leaks.
Interaction with Other Parts of the System
Core SWR Hook (
useSWR):
The subscription middleware composes on top of the core SWR hook for caching, mutation, and revalidation.Internal Utilities:
serializehandles consistent key serialization.createCacheHelperprovides setters and getters for manipulating cache entries safely.useIsomorphicLayoutEffectmanages effect lifecycles appropriately.
Subscription Types:
Defines contracts for the subscription function, options, and returned responses.Cache Provider:
The subscription state is scoped by the SWR cache instance, enabling multiple caches without collision.External Data Sources:
The module expects user-supplied subscription functions to handle actual connections to external event sources (WebSocket, event emitters, etc.).
Mermaid Diagram: Subscription Middleware Class-Like Structure
classDiagram
class SubscriptionMiddleware {
-subscriptionStorage: WeakMap<object, SubscriptionStates>
-SUBSCRIPTION_PREFIX: string = "$sub$"
+subscription<Data, Error>(
useSWRNext: SWRHook
) : (
_key: Key,
subscribe: SWRSubscription<any, Data, Error>,
config: SWRConfiguration & typeof SWRConfig.defaultValue
) => SWRSubscriptionResponse<Data, Error>
}
class SWRSubscriptionHook {
+useSWRSubscription<Key, Data, Error>(
key: Key,
subscribe: SWRSubscription<Key, Data, Error>,
config?: SWRConfiguration
) : SWRSubscriptionResponse<Data, Error>
}
SubscriptionMiddleware ..> SWRSubscriptionHook : composes
Summary
index.ts provides the subscription middleware and hook that extend the SWR core functionality to support real-time data subscriptions with efficient resource management. The middleware manages multiple subscribers through reference counting, ensures cache isolation, and integrates subscription updates cleanly into the SWR state and React lifecycle. The exported useSWRSubscription hook exposes a simple API for components to declaratively receive live updates from external data sources, enabling seamless integration of reactive data streams into React applications.
Appendix: Usage Example
import useSWRSubscription from 'swr/subscription'
function MyComponent({ userId }) {
const { data, error } = useSWRSubscription(
userId,
(id, { next }) => {
// Subscribe to user data updates from an external data source
const unsubscribe = userDataSource.subscribe(id, (err, userData) => {
next(err, userData)
})
return unsubscribe
},
{ refreshInterval: 0 } // optional SWR config
)
if (error) return <div>Error: {error.message}</div>
if (!data) return <div>Loading...</div>
return <div>User name: {data.name}</div>
}
This example demonstrates how a React component can subscribe to real-time user data updates using the useSWRSubscription hook.