Skip to content

Commit a0ae028

Browse files
committed
feat: add WithFallback component
1 parent b29f71e commit a0ae028

1 file changed

Lines changed: 42 additions & 0 deletions

File tree

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/* eslint-disable react/function-component-definition */
2+
3+
import { useEffect, useState } from 'react';
4+
5+
import { LoadingFallback } from './LoadingFallback';
6+
7+
const MIN_DELAY = 300; // ms
8+
9+
function isDataReady<TProps extends { data: unknown }>(
10+
props: TProps
11+
): props is TProps & { data: NonNullable<TProps['data']> } {
12+
return !(props.data === null || props.data === undefined);
13+
}
14+
15+
export function WithFallback<TProps extends { [key: string]: unknown }>({
16+
Component,
17+
props
18+
}: {
19+
Component: React.FC<TProps>;
20+
props: TProps extends { data: infer TData extends NonNullable<unknown> }
21+
? Omit<TProps, 'data'> & { data: null | TData | undefined }
22+
: never;
23+
}) {
24+
// if the data is not initially ready, set a min delay
25+
const [isMinDelayComplete, setIsMinDelayComplete] = useState(isDataReady(props));
26+
27+
useEffect(() => {
28+
let timeout: ReturnType<typeof setTimeout>;
29+
if (!isMinDelayComplete) {
30+
timeout = setTimeout(() => {
31+
setIsMinDelayComplete(true);
32+
}, MIN_DELAY);
33+
}
34+
return () => clearTimeout(timeout);
35+
}, []);
36+
37+
return isMinDelayComplete && isDataReady(props) ? (
38+
<Component {...(props as unknown as TProps)} />
39+
) : (
40+
<LoadingFallback />
41+
);
42+
}

0 commit comments

Comments
 (0)