-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAsyncView.tsx
More file actions
77 lines (68 loc) · 2.18 KB
/
AsyncView.tsx
File metadata and controls
77 lines (68 loc) · 2.18 KB
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
import { ReactElement, ReactNode } from 'react'
import { isFunction } from '../util'
type LoadingFunction = () => ReactNode
type SuccessFunction<Data> = (data: Data) => ReactNode
type ErrorFunction<Error> = (error: NonNullable<Error>) => ReactNode
type Props<Data, Error> = {
/**
* The async data to show.
* Note that `error` and `isLoading` take precedence over data.
*/
data: Data | undefined
/**
* Set an error to show the error state.
* Note that `isLoading` takes precedence over error.
*/
error: Error | undefined
/**
* Set to a boolean value to explicitly control loading state.
* If undefined, loading state is shown when there is no data (null | undefined) and no error (null | undefined).
*/
isLoading: boolean | undefined
renderLoading?: ReactNode | LoadingFunction
renderError?: ReactNode | ErrorFunction<Error>
} & (
| {
allowMissingData: true
renderSuccess: ReactNode | SuccessFunction<Data>
}
| {
allowMissingData?: false
renderSuccess: ReactNode | SuccessFunction<NonNullable<Data>>
}
)
const AsyncView = <Data, Error>(
props: Props<Data, Error>,
// The `ReactElement<any, any> | null` type is for React 17 compatibility (see type FunctionComponent). With React 18 it can be a ReactNode and we can remove the Fragment wrappers.
): ReactElement<any, any> | null => {
const {
data,
error,
isLoading,
renderLoading = null,
renderSuccess,
renderError = null,
allowMissingData = false,
} = props
const isError = error !== null && error !== undefined
const hasData = data !== null && data !== undefined
if (isLoading || (isLoading === undefined && !hasData && !isError)) {
return <>{isFunction(renderLoading) ? renderLoading() : renderLoading}</>
}
if (isError) {
return <>{isFunction(renderError) ? renderError(error) : renderError}</>
}
if (!hasData && !allowMissingData) {
throw new Error(
'Data passed into AsyncView was null or undefined. Use allowMissingData=true if this is intended.',
)
}
return (
<>
{isFunction(renderSuccess)
? renderSuccess(data as NonNullable<Data>)
: renderSuccess}
</>
)
}
export { AsyncView }