use-swr-infinite.test.tsx
Overview
This file contains a comprehensive suite of unit tests for the React hook useSWRInfinite from the SWR library. The useSWRInfinite hook is designed for managing infinite loading scenarios, such as paginated data fetching. These tests validate the hook’s behavior under various conditions, including initial rendering, pagination, mutation, caching, error handling, and configuration options.
The tests use React Testing Library for rendering components and simulating user interactions, and rely on utilities from a local ./utils module to create mock responses, keys, and control asynchronous behavior.
Detailed Description of Tests and Components
The file defines many test cases inside a describe('useSWRInfinite', () => { ... }) block. Each test defines a React component (Page, Comp, or Content) that uses the useSWRInfinite hook and renders its state, then asserts expected behaviors.
Key Imports
React hooks:
useEffect,useState,SuspenseTesting utilities:
fireEvent,act,screenfrom@testing-library/reactSWR hooks and utilities:
useSWRInfinite,mutate(aliased asglobalMutate),useSWRConfig,SWRConfigfor configurationLocal utilities:
sleep,createKey,createResponse,nextTick,renderWithConfig,renderWithGlobalCache,executeWithoutBatching
Core Concepts Tested
Basic rendering and data fetching: Confirm first page data is fetched and rendered.
Error handling: Ensure no render or crashes if
getKeythrows.Pagination: Loading multiple pages and appending data.
Mutations: Using
mutateto reload data or update cache with optimistic updates.Cursor-based pagination: Support for APIs using cursor/offset.
Cache behavior: Caching page counts, persistence across remounts and key changes.
Revalidation controls: Options like
revalidateFirstPage,dedupingInterval,refreshInterval.Parallel vs sequential requests: Control over parallel fetching of pages.
Fallback data: Starting with fallback cache data before fetch completes.
Sharing cache: Between multiple
useSWRInfinitehooks or withuseSWR.Advanced mutation options:
optimisticData,populateCache,rollbackOnError,throwOnError.Edge cases: Null keys, invalid setSize calls, and referential equality of setters.
Example Test Case Breakdown
Test: Basic rendering of first page
function Page() {
const { data, error, isValidating } = useSWRInfinite(
index => `page-${index}-${key}`,
infiniteKey => createResponse(infiniteKey)
)
return (
<div>
<div>data:{data}</div>
<div>isArray:{Array.isArray(data) ? 'true' : 'false'}</div>
<div>error:{error}</div>
<div>isValidating:{isValidating.toString()}</div>
</div>
)
}
Purpose: Confirm the hook fetches the first page and returns correct data state.
Parameters:
getKey: a function generating keys per page index.fetcher: returns a mocked response for each key.
Expected:
datacontains first page string, no error, not validating after fetch.
Important Implementation Details
The tests often simulate user interaction by clicking rendered elements to trigger
setSizeormutate.Uses
await screen.findByText()to asynchronously wait for UI updates after async fetches.Leverages
act()and customsleep()to flush React state changes and wait for async timing.Validates concurrency behaviors by checking how many requests are made, and cache reuse.
Tests
unstable_serializeutility for complex key serialization in mutations.Uses local utilities
renderWithConfigandrenderWithGlobalCacheto wrap components in SWR contexts with custom cache providers.
Interaction with Other System Parts
SWR core library: The tests directly exercise the
useSWRInfinitehook and related SWR APIs (mutate,useSWRConfig).React Testing Library: For rendering components and simulating events.
Local utilities (
./utils): Provide mock data, keys, and helpers to simulate asynchronous responses and manage test timing.SWRConfig Provider: Used in some tests to inject custom cache providers or configuration.
The file ensures useSWRInfinite behaves correctly as a data fetching hook in React apps, particularly focusing on pagination and cache management scenarios.
Functions and Components
Page (various versions)
Role: Test components using the
useSWRInfinitehook.Props: Usually none; internal state for toggling keys or page sizes.
Usage: Rendered inside tests to validate UI reflects data fetching and mutation states.
Example usage:
renderWithConfig(<Page />) await screen.findByText('data:page 0,')
Comp
Similar to
Page, often used to test mutation or cache persistence.
Utilities (from ./utils)
createKey(): Generates a unique key string for test isolation.createResponse(data, options?): Returns a Promise resolving todatawith optional delay.sleep(ms): Returns Promise that resolves aftermsmilliseconds.renderWithConfig(component, options?): Renders React component with SWR context and optional settings.renderWithGlobalCache(component): Renders component sharing global cache.executeWithoutBatching(fn): Runs function outside React batching to flush updates immediately.nextTick(): Helper to wait for the next event loop tick, allowing React state to settle.
Important Algorithms and Behaviors Tested
Sequential vs Parallel fetching: Tests verify if requests for pages happen one after another or in parallel based on the
paralleloption.Cache mutation and optimistic updates: Several tests show how optimistic UI updates work with rollback on error and how cache is updated or bypassed.
Key serialization and cache keys: Using
unstable_serializeto generate keys for cache mutation.Cursor-based pagination: Generating next keys based on previous page data to support offset cursors.
Referential equality: Ensuring setters (
setSize) are stable references across renders.Fallback data: Using initial data before fetch completes for faster UI display.
Revalidation logic: Controls when and how data revalidation occurs on mount, mutation, or size changes.
Visual Diagram
componentDiagram
component "useSWRInfinite Hook" as Hook {
+getKey(index, previousPageData)
+fetcher(key)
+data: Array
+error
+isValidating
+size
+setSize(size | fn)
+mutate(data?, options?)
}
component "Page / Comp Components" as PageComp
component "Testing Utils" as Utils {
+createKey()
+createResponse()
+sleep()
+renderWithConfig()
+renderWithGlobalCache()
}
component "SWRConfig Provider" as SWRConfig
PageComp --> Hook : uses
PageComp --> Utils : uses for mocks and render
Hook --> SWRConfig : reads config & cache
Hook --> SWRConfig : mutate function
Summary
File Purpose: To rigorously test the
useSWRInfiniteReact hook from SWR, verifying its correctness in infinite pagination, caching, mutation, and edge cases.Main Entities: Test React components (
Page,Comp), SWR hooks (useSWRInfinite), utilities for mocking and rendering.Relationships: Components use the hook; hook interacts with SWR cache and config; tests simulate user interaction and assert UI states.
Key Features Tested: Pagination, mutation, caching, error handling, concurrency, fallback data, key serialization, revalidation strategies, optimistic updates, rollback, and throw-on-error options.
This test file helps ensure that useSWRInfinite remains reliable and predictable across a wide range of usage scenarios in React applications.
Usage Example
A minimal example of using useSWRInfinite in a component as tested:
import useSWRInfinite from 'swr/infinite'
function InfiniteList() {
const { data, error, size, setSize, isValidating } = useSWRInfinite(
(index, previousPageData) => previousPageData && !previousPageData.length ? null : `api/data?page=${index + 1}`,
fetcherFunction,
{ initialSize: 1 }
)
if (error) return <div>Error: {error.message}</div>
if (!data) return <div>Loading...</div>
return (
<>
{data.map((page, i) => (
<div key={i}>{page.items.map(item => <p key={item.id}>{item.name}</p>)}</div>
))}
<button disabled={isValidating} onClick={() => setSize(size + 1)}>
Load More
</button>
</>
)
}
This matches the usage patterns thoroughly tested in the file.