Skip to content

Commit 1160cee

Browse files
committed
fix: re-design swr plugin structure
1 parent 9ca12f0 commit 1160cee

File tree

14 files changed

+1082
-1137
lines changed

14 files changed

+1082
-1137
lines changed

examples/openapi-ts-swr/src/App.tsx

Lines changed: 165 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,17 @@ import useSWRMutation from 'swr/mutation';
2121

2222
import { createClient } from './client/client';
2323
import { PetSchema } from './client/schemas.gen';
24+
import { getInventory } from './client/sdk.gen';
2425
import {
2526
addPetMutation,
27+
findPetsByStatusKey,
28+
findPetsByStatusOptions,
29+
getInventoryKey,
2630
getPetByIdOptions,
31+
loginUserKey,
2732
updatePetMutation,
2833
} from './client/swr.gen';
34+
// import { getPetByIdKey } from './client/swr.gen'; // For Pattern 2 example
2935
import type { Pet } from './client/types.gen';
3036

3137
const localClient = createClient({
@@ -59,8 +65,12 @@ function App() {
5965
const [pet, setPet] = useState<Pet>();
6066
const [petId, setPetId] = useState<number>();
6167
const [isRequiredNameError, setIsRequiredNameError] = useState(false);
68+
const [showAdvancedExamples, setShowAdvancedExamples] = useState(false);
6269

63-
// Mutations
70+
// ============================================================================
71+
// Mutations - using the generated mutation options
72+
// ============================================================================
73+
// The mutation options provide the key and fetcher following SWR best practices
6474
const { fetcher: addPetFetcher, key: addPetKey } = addPetMutation();
6575
const addPet = useSWRMutation(addPetKey, addPetFetcher, {
6676
onError: (error) => {
@@ -83,7 +93,11 @@ function App() {
8393
},
8494
});
8595

86-
// Query - only fetch if petId is set
96+
// ============================================================================
97+
// Pattern 1: Using Options (Recommended for most cases)
98+
// ============================================================================
99+
// The options provide both key and fetcher in the correct format
100+
// Conditional fetching is controlled by passing null to useSWR
87101
const petOptions = petId
88102
? getPetByIdOptions({
89103
client: localClient,
@@ -98,6 +112,75 @@ function App() {
98112
petOptions?.fetcher ?? null,
99113
);
100114

115+
// ============================================================================
116+
// Pattern 2: Using Key function directly (for custom fetchers)
117+
// ============================================================================
118+
// Key functions always return a valid key array, never null
119+
// This gives you full control over the fetcher while maintaining cache consistency
120+
//
121+
// Example (disabled to avoid duplicate requests):
122+
// const petByIdKey = petId ? getPetByIdKey({ path: { petId } }) : null;
123+
// const { data: customFetchedPet } = useSWR(petByIdKey, async (key) => {
124+
// if (!key) return null;
125+
// // Custom fetch logic here - you can add transforms, error handling, etc.
126+
// console.log('Fetching with key:', key);
127+
// const response = await fetch(`/api/pet/${key[1]}`);
128+
// return response.json();
129+
// });
130+
131+
// ============================================================================
132+
// Pattern 3: Optional parameters with optional chaining
133+
// ============================================================================
134+
// When options are optional, keys use optional chaining (options?.query)
135+
// This is safe and always returns a valid key
136+
const inventoryKey = getInventoryKey(); // No params needed
137+
const { data: inventory } = useSWR(
138+
showAdvancedExamples ? inventoryKey : null,
139+
async () => {
140+
// Custom fetcher - you control the implementation
141+
const { data } = await getInventory({
142+
client: localClient,
143+
throwOnError: true,
144+
});
145+
return data;
146+
},
147+
);
148+
149+
// ============================================================================
150+
// Pattern 4: Required parameters
151+
// ============================================================================
152+
// When parameters are required, options must be provided
153+
// The key function directly accesses options.query without optional chaining
154+
const petsByStatusKey = findPetsByStatusKey({
155+
query: { status: 'available' },
156+
});
157+
158+
// Or use the full options for convenience
159+
const { fetcher: petsByStatusFetcher, key: petsByStatusKey2 } =
160+
findPetsByStatusOptions({
161+
client: localClient,
162+
query: { status: 'available' },
163+
});
164+
165+
const { data: availablePets } = useSWR(
166+
showAdvancedExamples ? petsByStatusKey2 : null,
167+
showAdvancedExamples ? petsByStatusFetcher : null,
168+
);
169+
170+
// ============================================================================
171+
// Pattern 5: Demonstrating key equality for cache consistency
172+
// ============================================================================
173+
// Keys with the same parameters will have the same cache entry
174+
// This is a core SWR v2 improvement - primitive values in key arrays
175+
const loginKey1 = loginUserKey({
176+
query: { password: 'pass', username: 'test' },
177+
});
178+
const loginKey2 = loginUserKey({
179+
query: { password: 'pass', username: 'test' },
180+
});
181+
// loginKey1 and loginKey2 will be treated as the same cache key by SWR
182+
// because they have the same primitive values: ['/user/login', { username: 'test', password: 'pass' }]
183+
101184
const onAddPet = async (formData: FormData) => {
102185
// simple form field validation to demonstrate using schemas
103186
if (PetSchema.required.includes('name') && !formData.get('name')) {
@@ -180,7 +263,10 @@ function App() {
180263
<Heading>@hey-api/openapi-ts 🤝 SWR</Heading>
181264
</Flex>
182265
<Section size="1" />
266+
267+
{/* Main Demo Section */}
183268
<Flex direction="column" gapY="2">
269+
<Heading size="4">Basic Usage Demo</Heading>
184270
<Box maxWidth="240px">
185271
<Card>
186272
<Flex gap="3" align="center">
@@ -205,6 +291,66 @@ function App() {
205291
<DownloadIcon /> Get Random Pet
206292
</Button>
207293
</Flex>
294+
295+
<Section size="1" />
296+
297+
{/* Advanced Examples Toggle */}
298+
<Flex direction="column" gapY="2">
299+
<Button
300+
variant={showAdvancedExamples ? 'solid' : 'outline'}
301+
onClick={() => setShowAdvancedExamples(!showAdvancedExamples)}
302+
>
303+
{showAdvancedExamples ? 'Hide' : 'Show'} Advanced SWR v2 Examples
304+
</Button>
305+
306+
{showAdvancedExamples && (
307+
<Card>
308+
<Flex direction="column" gapY="2">
309+
<Heading size="3">SWR v2 Key Patterns</Heading>
310+
311+
<Box>
312+
<Text size="2" weight="bold">
313+
Inventory (Optional params):
314+
</Text>
315+
<Text size="1" color="gray">
316+
Key: {JSON.stringify(inventoryKey)}
317+
</Text>
318+
<Text size="1">
319+
Count:{' '}
320+
{inventory ? Object.keys(inventory).length : 'Loading...'}
321+
</Text>
322+
</Box>
323+
324+
<Box>
325+
<Text size="2" weight="bold">
326+
Available Pets (Required params):
327+
</Text>
328+
<Text size="1" color="gray">
329+
Key: {JSON.stringify(petsByStatusKey)}
330+
</Text>
331+
<Text size="1">
332+
Found: {availablePets?.length ?? 'Loading...'} pets
333+
</Text>
334+
</Box>
335+
336+
<Box>
337+
<Text size="2" weight="bold">
338+
Key Equality Demo:
339+
</Text>
340+
<Text size="1" color="gray">
341+
Key 1: {JSON.stringify(loginKey1)}
342+
</Text>
343+
<Text size="1" color="gray">
344+
Key 2: {JSON.stringify(loginKey2)}
345+
</Text>
346+
<Text size="1" color="green">
347+
✓ These keys are equal and share the same cache
348+
</Text>
349+
</Box>
350+
</Flex>
351+
</Card>
352+
)}
353+
</Flex>
208354
<Section size="1" />
209355
<Flex direction="column" gapY="2">
210356
<Form.Root
@@ -266,13 +412,24 @@ function App() {
266412
{/*
267413
useSWRInfinite Example (for paginated endpoints):
268414
269-
If your OpenAPI spec has pagination configured, you can use useSWRInfinite:
415+
If your OpenAPI spec has pagination configured, the SWR plugin generates
416+
infinite options functions (e.g., findPetsByStatusInfinite).
417+
418+
These functions return an object with:
419+
- getKey: Function that generates keys for each page
420+
- fetcher: Function that fetches a single page
421+
422+
Example usage:
270423
271424
import useSWRInfinite from 'swr/infinite';
272-
import { getPetsInfinite } from './client/swr.gen';
425+
import { findPetsByStatusInfinite } from './client/swr.gen';
273426
274427
function InfinitePetList() {
275-
const { getKey, fetcher } = getPetsInfinite();
428+
// Get the infinite options with your query parameters
429+
const { getKey, fetcher } = findPetsByStatusInfinite({
430+
query: { status: 'available' }
431+
});
432+
276433
const { data, size, setSize, isLoading } = useSWRInfinite(getKey, fetcher);
277434
278435
const pets = data ? data.flat() : [];
@@ -296,6 +453,9 @@ function App() {
296453
</div>
297454
);
298455
}
456+
457+
Note: The infinite options are only generated for operations that have
458+
pagination configured in the OpenAPI spec.
299459
*/}
300460
</Container>
301461
</Box>

0 commit comments

Comments
 (0)