use-swr-suspense.test.tsx
Overview
This file contains a comprehensive test suite for verifying the behavior of the useSWR React hook when used with React's Suspense mode enabled. useSWR is a popular data fetching hook from the SWR library designed to provide efficient, cached, and reactive data fetching for React applications.
Specifically, the tests target the interaction between useSWR and Suspense, ensuring that:
Suspense fallbacks are rendered correctly during data fetching.
Multiple concurrent Suspense-enabled SWR hooks behave as expected.
Error handling within Suspense boundaries works properly.
Cached data is used properly in Suspense mode.
Revalidation and fetching behaviors respect configuration flags.
State updates and key changes trigger the correct Suspense lifecycle.
Render optimizations prevent unnecessary re-renders.
The hook handles falsy keys and key transitions correctly.
Features like keepPreviousData work seamlessly with Suspense.
All tests employ React Testing Library utilities for rendering components and asserting DOM states, and use Jest mocks and timers for controlled async testing.
Detailed Explanations
Test Suite: describe('useSWR - suspense', () => { ... })
This suite contains multiple it test cases that validate different scenarios of using useSWR with Suspense enabled.
1. it('should render fallback', async () => { ... })
Purpose: Verify that the Suspense fallback UI is shown initially, and the resolved data renders after the fetch completes.
Implementation:
Defines a
Sectioncomponent that callsuseSWRwithsuspense: true.Wraps
Sectionin<Suspense fallback={<div>fallback</div>}>.Asserts that the fallback is initially shown, then the data (
'SWR') appears.
Usage: Basic test for Suspense fallback behavior.
2. it('should render multiple SWR fallbacks', async () => { ... })
Purpose: Test the behavior when multiple
useSWRhooks with Suspense are used simultaneously, each with delayed responses.Implementation:
Sectionuses twouseSWRhooks with different keys and 50ms delays.Suspense fallback should appear until both are resolved.
Checks that fallback is shown multiple times during staggered delays.
Asserts the sum of both resolved data is rendered (
3).
Key point: Suspense stacking and concurrent fetching handling.
3. it('should work for non-promises', async () => { ... })
Purpose: Ensure
useSWRworks with synchronous fetchers (non-promises) even with Suspense enabled.Implementation:
Fetcher returns a string immediately.
Suspense fallback is bypassed; data renders immediately.
Significance: Validates
useSWRhandles sync fetchers gracefully.
4. it('should throw errors', async () => { ... })
Purpose: Confirm that errors thrown during fetch are caught by an
ErrorBoundarywhen used with Suspense.Implementation:
Fetcher returns an error response.
Component wrapped in and .
Asserts fallback first, then error boundary UI renders.
Important: Demonstrates proper React Suspense + ErrorBoundary error handling.
5. it('should render cached data with error', async () => { ... })
Purpose: Check that cached data renders immediately even if the fetcher subsequently throws an error.
Implementation:
Cache is pre-populated with data (
'hello').Fetcher returns an error.
Expects cached data to render first, then error shown alongside cached data.
Note: Shows Suspense's interaction with stale cache and error states.
6. it('should not fetch when cached data is present and revalidateIfStale is false', async () => { ... })
Purpose: Ensure no re-fetch occurs if cached data exists and
revalidateIfStaleis disabled.Implementation:
Cache contains
'cached'.Fetcher increments a counter if called.
After waiting, confirms fetcher was never called.
Key feature: Revalidation control in Suspense mode.
7. it('should pause when key changes', async () => { ... })
Purpose: Verify that
useSWRsuspends rendering correctly when the key changes from one valid key to another.Implementation:
Stateful key toggling.
Uses React Profiler to count Suspense fallback renders.
Asserts fallback shown when key changes and final data renders.
Tested issue: Fixes a known bug with key change handled correctly by Suspense.
8. it('should render correctly when key changes (but with same response data)', async () => { ... })
Purpose: Ensures UI updates correctly when the key changes but fetch returns same data.
Implementation:
Tracks rendered results to assert distinct renders for different keys.
Click to increment key triggers new fetch.
Point: Validates React’s key-based cache invalidation and Suspense rendering.
9. it('should render correctly when key changes (from null to valid key)', async () => { ... })
Purpose: Test transitioning from no fetch (null key) to a valid key and back.
Implementation:
Component conditionally fetches depending on query prop.
Uses Suspense with delayed fetches.
Tracks rendered outputs to validate behavior.
Importance: Handles null keys properly in Suspense mode.
10. it('should render initial data if set', async () => { ... })
Purpose: Confirm that if fallbackData is provided, fetcher is not called and fallback data renders immediately.
Implementation:
Fetcher mocked to track calls.
Initial render shows fallback data, fetcher not invoked.
Optimization: Avoids unnecessary fetch when initial data exists.
11. it('should avoid unnecessary re-renders', async () => { ... })
Purpose: Verify that components using
useSWRwith Suspense do not re-render excessively.Implementation:
Counts render calls before and after hydration.
Uses sleep to observe renders.
Result: Suspense triggers minimal renders, optimizing performance.
12. it('should return [undefined](/projects/308/70886) data for falsy key', async () => { ... })
Purpose: Tests that falsy keys (e.g., null) cause
useSWRto return undefined and not fetch.Implementation:
Toggles a boolean trigger to switch key between valid and null.
Validates fallback and data rendering accordingly.
Behavior: Correctly handles conditional fetching under Suspense.
13. it('should only render fallback once when [keepPreviousData](/projects/308/70921) is true', async () => { ... })
Purpose: Ensure that when keepPreviousData is enabled, Suspense fallback UI renders only once during key change.
Implementation:
Uses Profiler to count fallback render calls.
Switches keys with delayed fetchers.
Benefit: Smooth UI transitions without flickering fallback.
Important Implementation Details
Suspense Integration: All tests use React's component with a fallback UI to test Suspense-driven loading states.
Asynchronous Testing: Uses
async/await,act(), and helper functions like sleep() to simulate delays and await UI updates.Cache Manipulation: Uses
mutate()from SWR to prefill cache for testing cached data scenarios.Error Handling: Employs
ErrorBoundaryfrom react-error-boundary to test error propagation inside Suspense.React Profiler: Used in some tests to count component render cycles and validate render optimizations.
Key Management: Several tests manipulate the fetch key dynamically to test Suspense's behavior on key changes.
Test Utilities: Custom helpers like
createKey(),createResponse(),renderWithConfig(), and renderWithGlobalCache() abstract away setup and data mocking details to focus on core test logic.
Interaction with Other System Parts
SWR Hook (
useSWR): Central dependency; these tests verify its Suspense mode features.React Suspense & ErrorBoundary: These React features are critical for the tested Suspense behaviors.
Testing Utilities (
./utils): Provide key creation, mock responses, rendering wrappers, and timing control.React Testing Library: For rendering components and querying the DOM.
Jest: For mocking, spies, and assertions.
The file is a test file and thus does not impact production code directly but validates critical data-fetching and UI-suspense integration logic that underpins the application's data layer and user experience.
Usage Examples
Each test defines a React component example inside the test block. For instance:
function Section() {
const { data } = useSWR('key', () => createResponse('SWR'), {
suspense: true
})
return <div>{data}</div>
}
renderWithConfig(
<Suspense fallback={<div>fallback</div>}>
<Section />
</Suspense>
)
This pattern illustrates how to:
Use
useSWRwith suspense enabled.Wrap components in Suspense fallback UI.
Assert UI state transitions from fallback to loaded data.
Visual Diagram
flowchart TD
A[useSWR Suspense Tests] --> B[Render Section Component]
B --> C{useSWR Hook}
C -->|With Suspense| D[Suspense Fallback Rendered]
C -->|Data Available| E[Data Rendered]
C -->|Error Thrown| F[ErrorBoundary Catch]
B --> G[Cache Manipulation via mutate()]
B --> H[State/Key Updates]
H --> C
B --> I[Profiler for Render Count]
B --> J[fireEvent for UI Interaction]
D -->|Await Data| E
F -->|Show Error UI| E
Diagram Explanation:
The main flow involves rendering a component that uses
useSWRwith Suspense.Depending on the data fetching state, Suspense fallback UI, data UI, or error boundary UI is rendered.
Tests manipulate cache, update keys, and use profiler and events to simulate real-world interactions and verify rendering behavior.
Summary
This test file is essential for validating the correct integration of SWR's data fetching hook with React Suspense features. It covers a broad spectrum of cases including normal loading, multiple concurrent fetches, error states, caching behaviors, and render optimizations. The tests ensure that Suspense fallback UIs and error boundaries behave as expected, and that useSWR configurations like revalidateIfStale and keepPreviousData influence Suspense rendering correctly. This guarantees a smooth and performant data-driven React application UI experience.