Skip to content

Commit 6e3f837

Browse files
committed
feat(ui): enhance DataFetcher with retry visibility and exponential backoff
1 parent bdeffa6 commit 6e3f837

File tree

1 file changed

+40
-4
lines changed

1 file changed

+40
-4
lines changed

apps/web/src/components/ui/DataFetcher.tsx

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,13 @@ export interface DataFetcherConfig<T> {
3232
successMessage?: string;
3333
/** Whether to show error toast on failure */
3434
showErrorToast?: boolean;
35+
/** Whether to show retry status in toasts */
36+
showRetryStatus?: boolean;
3537
/** Retry configuration */
3638
retry?: {
3739
maxAttempts: number;
3840
delay: number;
41+
backoffMultiplier?: number;
3942
};
4043
/** Loading skeleton type */
4144
skeletonType?: "text" | "card" | "list" | "table" | "graph" | "stats";
@@ -99,12 +102,33 @@ export const useDataFetcher = <T,>(config: DataFetcherConfig<T>) => {
99102
toast.error(config.errorMessage || errorObj.message);
100103
}
101104

102-
// Handle retry logic
105+
// Handle retry logic with enhanced visibility
103106
if (config.retry && retryCount < config.retry.maxAttempts) {
104-
setRetryCount((prev) => prev + 1);
107+
const nextRetryCount = retryCount + 1;
108+
const backoffMultiplier = config.retry.backoffMultiplier || 1;
109+
const delay = config.retry.delay * Math.pow(backoffMultiplier, retryCount);
110+
111+
setRetryCount(nextRetryCount);
112+
113+
// Show retry status toast if enabled
114+
if (config.showRetryStatus) {
115+
const remainingAttempts = config.retry.maxAttempts - nextRetryCount;
116+
toast.info(
117+
`Retrying... Attempt ${nextRetryCount} of ${config.retry.maxAttempts} (${remainingAttempts} remaining)`,
118+
{ autoClose: delay }
119+
);
120+
}
121+
105122
setTimeout(() => {
106123
executeFetch();
107-
}, config.retry.delay);
124+
}, delay);
125+
} else if (config.retry && retryCount >= config.retry.maxAttempts) {
126+
// Show final failure message when all retries exhausted
127+
if (config.showErrorToast) {
128+
toast.error(
129+
config.errorMessage || `Failed after ${config.retry.maxAttempts} attempts. Please try again later.`
130+
);
131+
}
108132
}
109133

110134
throw errorObj;
@@ -302,9 +326,11 @@ export const DataFetcherConfigs = {
302326
skeletonCount: 5,
303327
showSuccessToast: false,
304328
showErrorToast: true,
329+
showRetryStatus: true,
305330
retry: {
306331
maxAttempts: 3,
307332
delay: 1000,
333+
backoffMultiplier: 1.5,
308334
},
309335
},
310336

@@ -314,7 +340,13 @@ export const DataFetcherConfigs = {
314340
skeletonCount: 6,
315341
showSuccessToast: false,
316342
showErrorToast: true,
343+
showRetryStatus: true,
317344
errorMessage: "Search failed. Please try again.",
345+
retry: {
346+
maxAttempts: 2,
347+
delay: 800,
348+
backoffMultiplier: 2,
349+
},
318350
},
319351

320352
/** Configuration for entity details */
@@ -323,9 +355,11 @@ export const DataFetcherConfigs = {
323355
skeletonCount: 1,
324356
showSuccessToast: false,
325357
showErrorToast: true,
358+
showRetryStatus: true,
326359
retry: {
327360
maxAttempts: 2,
328361
delay: 500,
362+
backoffMultiplier: 1.5,
329363
},
330364
},
331365

@@ -336,9 +370,11 @@ export const DataFetcherConfigs = {
336370
showSuccessToast: true,
337371
successMessage: "Graph data loaded successfully",
338372
showErrorToast: true,
373+
showRetryStatus: true,
339374
retry: {
340-
maxAttempts: 1,
375+
maxAttempts: 2,
341376
delay: 2000,
377+
backoffMultiplier: 1.2,
342378
},
343379
},
344380
} as const;

0 commit comments

Comments
 (0)