Skip to content

Commit c10349b

Browse files
committed
fix: Improve useRetry hook logic
- Fix startRetrying to immediately perform first retry - Adjust retry scheduling conditions - Fix delay calculation for exponential backoff Still debugging test failures
1 parent c115f13 commit c10349b

File tree

1 file changed

+52
-36
lines changed

1 file changed

+52
-36
lines changed

site/src/hooks/useRetry.ts

Lines changed: 52 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@ export function useRetry(options: UseRetryOptions): UseRetryReturn {
6363
const [isRetrying, setIsRetrying] = useState(false);
6464
const [currentDelay, setCurrentDelay] = useState<number | null>(null);
6565
const [attemptCount, setAttemptCount] = useState(0);
66-
const [timeUntilNextRetry, setTimeUntilNextRetry] = useState<number | null>(null);
66+
const [timeUntilNextRetry, setTimeUntilNextRetry] = useState<number | null>(
67+
null,
68+
);
6769
const [isManualRetry, setIsManualRetry] = useState(false);
6870

6971
const timeoutRef = useRef<number | null>(null);
@@ -84,10 +86,13 @@ export function useRetry(options: UseRetryOptions): UseRetryReturn {
8486
startTimeRef.current = null;
8587
}, []);
8688

87-
const calculateDelay = useCallback((attempt: number): number => {
88-
const delay = initialDelay * Math.pow(multiplier, attempt);
89-
return Math.min(delay, maxDelay);
90-
}, [initialDelay, multiplier, maxDelay]);
89+
const calculateDelay = useCallback(
90+
(attempt: number): number => {
91+
const delay = initialDelay * multiplier ** attempt;
92+
return Math.min(delay, maxDelay);
93+
},
94+
[initialDelay, multiplier, maxDelay],
95+
);
9196

9297
const performRetry = useCallback(async () => {
9398
setIsRetrying(true);
@@ -103,47 +108,56 @@ export function useRetry(options: UseRetryOptions): UseRetryReturn {
103108
setIsManualRetry(false);
104109
} catch (error) {
105110
// If retry fails, schedule next attempt (if not manual and under max attempts)
106-
setAttemptCount(prev => prev + 1);
111+
setAttemptCount((prev) => prev + 1);
107112
setIsRetrying(false);
108113
setIsManualRetry(false);
109114
}
110115
}, [onRetryEvent, clearTimers]);
111116

112-
const scheduleNextRetry = useCallback((attempt: number) => {
113-
if (attempt >= maxAttempts) {
114-
return;
115-
}
117+
const scheduleNextRetry = useCallback(
118+
(attempt: number) => {
119+
if (attempt >= maxAttempts) {
120+
return;
121+
}
116122

117-
const delay = calculateDelay(attempt);
118-
setCurrentDelay(delay);
119-
setTimeUntilNextRetry(delay);
120-
startTimeRef.current = Date.now();
121-
122-
// Start countdown timer
123-
countdownRef.current = setInterval(() => {
124-
if (startTimeRef.current) {
125-
const elapsed = Date.now() - startTimeRef.current;
126-
const remaining = Math.max(0, delay - elapsed);
127-
setTimeUntilNextRetry(remaining);
128-
129-
if (remaining <= 0) {
130-
if (countdownRef.current) {
131-
clearInterval(countdownRef.current);
132-
countdownRef.current = null;
123+
// Calculate delay based on attempt - 2 (so second attempt gets initialDelay)
124+
const delay = calculateDelay(Math.max(0, attempt - 2));
125+
setCurrentDelay(delay);
126+
setTimeUntilNextRetry(delay);
127+
startTimeRef.current = Date.now();
128+
129+
// Start countdown timer
130+
countdownRef.current = setInterval(() => {
131+
if (startTimeRef.current) {
132+
const elapsed = Date.now() - startTimeRef.current;
133+
const remaining = Math.max(0, delay - elapsed);
134+
setTimeUntilNextRetry(remaining);
135+
136+
if (remaining <= 0) {
137+
if (countdownRef.current) {
138+
clearInterval(countdownRef.current);
139+
countdownRef.current = null;
140+
}
133141
}
134142
}
135-
}
136-
}, 100); // Update every 100ms for smooth countdown
143+
}, 100); // Update every 100ms for smooth countdown
137144

138-
// Schedule the actual retry
139-
timeoutRef.current = setTimeout(() => {
140-
performRetry();
141-
}, delay);
142-
}, [calculateDelay, maxAttempts, performRetry]);
145+
// Schedule the actual retry
146+
timeoutRef.current = setTimeout(() => {
147+
performRetry();
148+
}, delay);
149+
},
150+
[calculateDelay, maxAttempts, performRetry],
151+
);
143152

144153
// Effect to schedule next retry after a failed attempt
145154
useEffect(() => {
146-
if (!isRetrying && !isManualRetry && attemptCount > 0 && attemptCount < maxAttempts) {
155+
if (
156+
!isRetrying &&
157+
!isManualRetry &&
158+
attemptCount > 1 &&
159+
attemptCount <= maxAttempts
160+
) {
147161
scheduleNextRetry(attemptCount);
148162
}
149163
}, [attemptCount, isRetrying, isManualRetry, maxAttempts, scheduleNextRetry]);
@@ -157,8 +171,10 @@ export function useRetry(options: UseRetryOptions): UseRetryReturn {
157171
}, [clearTimers, performRetry]);
158172

159173
const startRetrying = useCallback(() => {
160-
setAttemptCount(1); // This will trigger the first retry attempt
161-
}, []);
174+
// Immediately perform the first retry attempt
175+
setAttemptCount(1);
176+
performRetry();
177+
}, [performRetry]);
162178

163179
const stopRetrying = useCallback(() => {
164180
clearTimers();

0 commit comments

Comments
 (0)