Skip to content

Commit 09de9e3

Browse files
committed
fix: optimize request function call in withRetry and add integration test for POST body cache key update
1 parent fd5d208 commit 09de9e3

File tree

2 files changed

+71
-1
lines changed

2 files changed

+71
-1
lines changed

src/retry-handler.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,10 @@ export async function withRetry<
140140
}
141141
}
142142

143-
output = await requestFn(true, attempt); // isStaleRevalidation=false, isFirstAttempt=attempt===0
143+
// Performance optimization: Call the request function with the current attempt number
144+
// If this is the first attempt, we pass `isStaleRevalidation` as `false`,
145+
// otherwise we pass `true` to indicate that this is a stale revalidation (no cache hit).
146+
output = await requestFn(attempt > 0, attempt);
144147
const error = output.error;
145148

146149
// Check if we should retry based on successful response
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/**
2+
* @jest-environment jsdom
3+
*/
4+
import { useState } from 'react';
5+
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
6+
import { mockFetchResponse } from '../../utils/mockFetchResponse';
7+
import { useFetcher } from '../../../src/react/index';
8+
9+
describe('POST body cache key update and refetch', () => {
10+
it('uses new body on refetch, keeps static cache key, and updates UI state', async () => {
11+
mockFetchResponse('/api/user', {
12+
ok: true,
13+
status: 200,
14+
body: { echoed: { name: 'Alice' } },
15+
});
16+
17+
function TestComponent() {
18+
const [name, setName] = useState('Alice');
19+
const { data, refetch, isLoading, isFetching, config } = useFetcher(
20+
'/api/user',
21+
{
22+
method: 'POST',
23+
body: { name },
24+
cacheKey: '/api/user',
25+
immediate: true,
26+
retry: { retries: 5, delay: 1000, backoff: 2 },
27+
},
28+
);
29+
return (
30+
<div>
31+
<div data-testid="result">{data?.echoed?.name}</div>
32+
<div data-testid="config">{JSON.stringify(config)}</div>
33+
<div data-testid="loading">{isLoading ? 'loading' : 'idle'}</div>
34+
<div data-testid="fetching">{isFetching ? 'fetching' : 'idle'}</div>
35+
<button onClick={() => setName('Bob')}>Change Name</button>
36+
<button onClick={() => refetch()}>Refetch</button>
37+
</div>
38+
);
39+
}
40+
41+
render(<TestComponent />);
42+
await waitFor(() =>
43+
expect(screen.getByTestId('result').textContent).toBe('Alice'),
44+
);
45+
46+
mockFetchResponse('/api/user', {
47+
ok: true,
48+
status: 200,
49+
body: { echoed: { name: 'Bob' } },
50+
});
51+
fireEvent.click(screen.getByText('Change Name'));
52+
fireEvent.click(screen.getByText('Refetch'));
53+
expect(screen.getByTestId('loading').textContent).toBe('loading');
54+
expect(screen.getByTestId('fetching').textContent).toBe('fetching');
55+
await waitFor(() =>
56+
expect(screen.getByTestId('result').textContent).toBe('Bob'),
57+
);
58+
expect(screen.getByTestId('loading').textContent).toBe('idle');
59+
expect(screen.getByTestId('fetching').textContent).toBe('idle');
60+
expect(screen.getByTestId('config').textContent).toContain(
61+
'"body":"{\\"name\\":\\"Bob\\"}"',
62+
);
63+
expect(screen.getByTestId('config').textContent).toContain(
64+
'"cacheKey":"/api/user"',
65+
);
66+
});
67+
});

0 commit comments

Comments
 (0)