78 lines
1.7 KiB
TypeScript

import { Button, Spinner } from "@fluentui/react-components";
import React, { useEffect, useRef, useState } from "react";
enum State {
Loading,
Ready,
Error,
}
export function AsyncWidget(p: {
loadKey: any;
load: () => Promise<void>;
errMsg: string;
build: () => React.ReactElement;
ready?: boolean;
buildLoading?: () => React.ReactElement;
buildError?: (e: string) => React.ReactElement;
errAdditionalElement?: () => React.ReactElement;
loadingMessage?: string;
}): React.ReactElement {
const [state, setState] = useState(State.Loading);
const counter = useRef<any | null>(null);
const load = async () => {
try {
setState(State.Loading);
await p.load();
setState(State.Ready);
} catch (e) {
console.error(e);
setState(State.Error);
}
};
useEffect(() => {
if (counter.current === p.loadKey) return;
counter.current = p.loadKey;
load();
});
if (state === State.Error)
return (
p.buildError?.(p.errMsg) ?? (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100%",
flex: "1",
flexDirection: "column",
}}
>
<div style={{ margin: "50px" }}>{p.errMsg}</div>
<Button onClick={load}>Try again</Button>
{p.errAdditionalElement && p.errAdditionalElement()}
</div>
)
);
if (state === State.Loading || p.ready === false)
return (
p.buildLoading?.() ?? (
<Spinner
labelPosition="below"
label={p.loadingMessage}
style={{ margin: "auto" }}
/>
)
);
return p.build();
}