Skip to content

Commit 4fd4885

Browse files
BrunoQuaresmaclaude
andcommitted
fix(hooks): update useWithRetry tests for nextRetryAt API and add useEffectEvent
- Update all test references from retryAt to nextRetryAt to match the new API - Add useEffectEvent import to stabilize function reference and prevent unnecessary re-renders - Update callback dependency from fn to stableFn for better performance - All tests now pass with the updated API 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent f9832c0 commit 4fd4885

File tree

2 files changed

+26
-23
lines changed

2 files changed

+26
-23
lines changed

site/src/hooks/useWithRetry.test.ts

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ describe("useWithRetry", () => {
2020
const { result } = renderHook(() => useWithRetry(mockFn));
2121

2222
expect(result.current.isLoading).toBe(false);
23-
expect(result.current.retryAt).toBe(undefined);
23+
expect(result.current.nextRetryAt).toBe(undefined);
2424
});
2525

2626
it("should execute function successfully on first attempt", async () => {
@@ -34,7 +34,7 @@ describe("useWithRetry", () => {
3434

3535
expect(mockFn).toHaveBeenCalledTimes(1);
3636
expect(result.current.isLoading).toBe(false);
37-
expect(result.current.retryAt).toBe(undefined);
37+
expect(result.current.nextRetryAt).toBe(undefined);
3838
});
3939

4040
it("should set isLoading to true during execution", async () => {
@@ -75,7 +75,7 @@ describe("useWithRetry", () => {
7575

7676
expect(mockFn).toHaveBeenCalledTimes(1);
7777
expect(result.current.isLoading).toBe(false);
78-
expect(result.current.retryAt).not.toBe(null);
78+
expect(result.current.nextRetryAt).not.toBe(null);
7979

8080
// Fast-forward to first retry (1 second)
8181
await act(async () => {
@@ -84,7 +84,7 @@ describe("useWithRetry", () => {
8484

8585
expect(mockFn).toHaveBeenCalledTimes(2);
8686
expect(result.current.isLoading).toBe(false);
87-
expect(result.current.retryAt).not.toBe(null);
87+
expect(result.current.nextRetryAt).not.toBe(null);
8888

8989
// Fast-forward to second retry (2 seconds)
9090
await act(async () => {
@@ -93,7 +93,7 @@ describe("useWithRetry", () => {
9393

9494
expect(mockFn).toHaveBeenCalledTimes(3);
9595
expect(result.current.isLoading).toBe(false);
96-
expect(result.current.retryAt).toBe(undefined);
96+
expect(result.current.nextRetryAt).toBe(undefined);
9797
});
9898

9999
it("should continue retrying without limit", async () => {
@@ -108,7 +108,7 @@ describe("useWithRetry", () => {
108108

109109
expect(mockFn).toHaveBeenCalledTimes(1);
110110
expect(result.current.isLoading).toBe(false);
111-
expect(result.current.retryAt).not.toBe(null);
111+
expect(result.current.nextRetryAt).not.toBe(null);
112112

113113
// Fast-forward through multiple retries to verify it continues
114114
for (let i = 1; i < 15; i++) {
@@ -118,11 +118,11 @@ describe("useWithRetry", () => {
118118
});
119119
expect(mockFn).toHaveBeenCalledTimes(i + 1);
120120
expect(result.current.isLoading).toBe(false);
121-
expect(result.current.retryAt).not.toBe(null);
121+
expect(result.current.nextRetryAt).not.toBe(null);
122122
}
123123

124124
// Should still be retrying after 15 attempts
125-
expect(result.current.retryAt).not.toBe(null);
125+
expect(result.current.nextRetryAt).not.toBe(null);
126126
});
127127

128128
it("should respect max delay of 10 minutes", async () => {
@@ -150,7 +150,7 @@ describe("useWithRetry", () => {
150150
}
151151

152152
expect(mockFn).toHaveBeenCalledTimes(9);
153-
expect(result.current.retryAt).not.toBe(null);
153+
expect(result.current.nextRetryAt).not.toBe(null);
154154

155155
// The 9th retry should use max delay (600000ms = 10 minutes)
156156
await act(async () => {
@@ -159,15 +159,15 @@ describe("useWithRetry", () => {
159159

160160
expect(mockFn).toHaveBeenCalledTimes(10);
161161
expect(result.current.isLoading).toBe(false);
162-
expect(result.current.retryAt).not.toBe(null);
162+
expect(result.current.nextRetryAt).not.toBe(null);
163163

164164
// Continue with more retries at max delay to verify it continues indefinitely
165165
await act(async () => {
166166
jest.advanceTimersByTime(600000);
167167
});
168168

169169
expect(mockFn).toHaveBeenCalledTimes(11);
170-
expect(result.current.retryAt).not.toBe(null);
170+
expect(result.current.nextRetryAt).not.toBe(null);
171171
});
172172

173173
it("should cancel previous retry when call is invoked again", async () => {
@@ -184,7 +184,7 @@ describe("useWithRetry", () => {
184184

185185
expect(mockFn).toHaveBeenCalledTimes(1);
186186
expect(result.current.isLoading).toBe(false);
187-
expect(result.current.retryAt).not.toBe(null);
187+
expect(result.current.nextRetryAt).not.toBe(null);
188188

189189
// Call again before retry happens
190190
await act(async () => {
@@ -193,7 +193,7 @@ describe("useWithRetry", () => {
193193

194194
expect(mockFn).toHaveBeenCalledTimes(2);
195195
expect(result.current.isLoading).toBe(false);
196-
expect(result.current.retryAt).toBe(undefined);
196+
expect(result.current.nextRetryAt).toBe(undefined);
197197

198198
// Advance time to ensure previous retry was cancelled
199199
await act(async () => {
@@ -203,7 +203,7 @@ describe("useWithRetry", () => {
203203
expect(mockFn).toHaveBeenCalledTimes(2); // Should not have been called again
204204
});
205205

206-
it("should set retryAt when scheduling retry", async () => {
206+
it("should set nextRetryAt when scheduling retry", async () => {
207207
mockFn
208208
.mockRejectedValueOnce(new Error("Failure"))
209209
.mockResolvedValueOnce(undefined);
@@ -215,21 +215,21 @@ describe("useWithRetry", () => {
215215
await result.current.call();
216216
});
217217

218-
const retryAt = result.current.retryAt;
219-
expect(retryAt).not.toBe(null);
220-
expect(retryAt).toBeInstanceOf(Date);
218+
const nextRetryAt = result.current.nextRetryAt;
219+
expect(nextRetryAt).not.toBe(null);
220+
expect(nextRetryAt).toBeInstanceOf(Date);
221221

222-
// retryAt should be approximately 1 second in the future
222+
// nextRetryAt should be approximately 1 second in the future
223223
const expectedTime = Date.now() + 1000;
224-
const actualTime = retryAt!.getTime();
224+
const actualTime = nextRetryAt!.getTime();
225225
expect(Math.abs(actualTime - expectedTime)).toBeLessThan(100); // Allow 100ms tolerance
226226

227227
// Advance past retry time
228228
await act(async () => {
229229
jest.advanceTimersByTime(1000);
230230
});
231231

232-
expect(result.current.retryAt).toBe(undefined);
232+
expect(result.current.nextRetryAt).toBe(undefined);
233233
});
234234

235235
it("should cleanup timer on unmount", async () => {
@@ -243,7 +243,7 @@ describe("useWithRetry", () => {
243243
});
244244

245245
expect(result.current.isLoading).toBe(false);
246-
expect(result.current.retryAt).not.toBe(null);
246+
expect(result.current.nextRetryAt).not.toBe(null);
247247

248248
// Unmount should cleanup timer
249249
unmount();

site/src/hooks/useWithRetry.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useCallback, useEffect, useRef, useState } from "react";
2+
import { useEffectEvent } from "./hookPolyfills";
23

34
const DELAY_MS = 1_000;
45
const MAX_DELAY_MS = 600_000; // 10 minutes
@@ -36,6 +37,8 @@ export function useWithRetry(fn: () => Promise<void>): UseWithRetryResult {
3637
}
3738
}, []);
3839

40+
const stableFn = useEffectEvent(fn)
41+
3942
const call = useCallback(async () => {
4043
clearTimeout();
4144

@@ -46,7 +49,7 @@ export function useWithRetry(fn: () => Promise<void>): UseWithRetryResult {
4649
});
4750

4851
try {
49-
await fn();
52+
await stableFn();
5053
setState({ isLoading: false, nextRetryAt: undefined });
5154
} catch (error) {
5255
const delayMs = Math.min(
@@ -70,7 +73,7 @@ export function useWithRetry(fn: () => Promise<void>): UseWithRetryResult {
7073
};
7174

7275
await executeAttempt(0);
73-
}, [fn, clearTimeout]);
76+
}, [stableFn, clearTimeout]);
7477

7578
useEffect(() => {
7679
return () => {

0 commit comments

Comments
 (0)