-
Notifications
You must be signed in to change notification settings - Fork 10
/
retry-helper.tsx
106 lines (100 loc) · 2.62 KB
/
retry-helper.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import * as Sentry from '@sentry/react';
import {
ErrorBoundaryFallback,
errorBoundaryFallbackFunction,
} from 'components/uikit/suspense/ErrorBoundary';
import { Loading } from 'components/uikit/suspense/Loading';
import * as React from 'react';
import {
ComponentType,
lazy,
PropsWithChildren,
Suspense,
useCallback,
useMemo,
useState,
} from 'react';
import { QueryErrorResetBoundary } from '@tanstack/react-query';
/**
* Helper which retries to run a given async function if it is failed.
*/
export function retry<T>(
fn: () => Promise<T>,
retriesLeft = 2,
intervalInMillis = 1000,
): Promise<T> {
return new Promise((resolve, reject) => {
fn()
.then(resolve)
.catch((reason) => {
if (retriesLeft === 0) {
reject(reason);
return;
}
setTimeout(() => {
retry<T>(fn, retriesLeft - 1, intervalInMillis).then(resolve, reject);
}, intervalInMillis);
});
});
}
/**
* Helper which retries to load a lazy component if it is failed.
* Wrapped in ErrorBoundary to show an error and reload if something bad happens
*/
export function lazyRetry<T extends ComponentType<any>>(
factory: () => Promise<{ default: T }>,
): T {
const RetryWrapper = (props: any) => {
const [loading, setLoading] = useState(true);
const resetError = useCallback(() => setLoading(true), []);
const LazyComponent = useMemo(
() =>
lazy(() =>
factory().catch((e) => {
setLoading(false);
return {
default: function Fallback() {
return (
<ErrorBoundaryFallback
resetError={resetError}
error={e}
componentStack={null}
eventId={null}
/>
);
},
} as any;
}),
),
[factory, loading],
);
return (
<QuerySuspenseErrorWrapper>
<LazyComponent {...props} />
</QuerySuspenseErrorWrapper>
);
};
return RetryWrapper as any;
}
export const QuerySuspenseErrorWrapper: React.FC<
PropsWithChildren<{ reset?: () => void }>
> = (props) => {
const result: any = (
<QueryErrorResetBoundary>
{({ reset }) => (
<Sentry.ErrorBoundary
fallback={errorBoundaryFallbackFunction}
onReset={() => {
reset();
props.reset?.();
}}
>
<Suspense fallback={<Loading loading={true} />}>
{props.children}
</Suspense>
</Sentry.ErrorBoundary>
)}
</QueryErrorResetBoundary>
);
return result;
};